ios - 从 block 更新 uilabel

标签 ios iphone objective-c objective-c-blocks

从相册中导入多张照片,委托(delegate)方法之一是

// Here info is array of dictionary containing FileName/AssetURL etc
- (void)somePicker(SomePicker*)somePicker didFinishPickingMediaWithInfo:(NSArray *)info {
    _importStatusView.center = self.view.center;
    [self.view addSubview:_importStatusView];
    [self dismissViewControllerAnimated:YES completion:^{
        NSNumber *total = [NSNumber numberWithInteger:info.count];
        [info enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSDictionary *imageInfo = (NSDictionary*)obj;
            NSString *fileName = [imageInfo objectForKey:@"UIImagePickerControllerFileName"];
            NSURL *imageURL = [imageInfo objectForKey:UIImagePickerControllerReferenceURL];
            ALAssetsLibrary *assetLibrary=[[ALAssetsLibrary alloc] init];
            [assetLibrary assetForURL:imageURL resultBlock:^(ALAsset *asset) {
                NSLog(@"start");
                ALAssetRepresentation *rep = [asset defaultRepresentation];
                Byte *buffer = (Byte*)malloc(rep.size);
                NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
                NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
                NSString *filePath = [_currentPath stringByAppendingPathComponent:fileName];
                [data writeToFile:filePath atomically:YES];

                //This also has no effect 
                //dispatch_async(dispatch_get_main_queue(), ^{
                    //_lblImportCountStatus.text = [NSString stringWithFormat:@"%d of %d",idx+1,[total integerValue]];
                    //NSLog(@"Label value->%@",_lblImportCountStatus.text); //This prints values but after everything is finished it prints all line at once i.e. at the end of the enumeration of all items 
                //});

            //Update UI
            NSNumber *current = [NSNumber numberWithInteger:idx+1];
            NSDictionary *status = [NSDictionary dictionaryWithObjectsAndKeys:current,@"current", total,@"totalCount", nil];
            [self performSelectorOnMainThread:@selector(updateImportCount:) withObject:status waitUntilDone:YES];

                //_lblImportCountStatus.text = [NSString stringWithFormat:@"%d of %d",idx+1,[total integerValue]];
                if(idx==info.count-1){
                    [_importStatusView removeFromSuperview];
                }

                NSLog(@"Finish");
            } failureBlock:^(NSError *error) {
                NSLog(@"Error: %@",[error localizedDescription]);
            }];
        }];
    }];
}

我对状态 View 和标签的声明是

@property (strong, nonatomic) IBOutlet UIView *importStatusView; //View containing label 
@property (weak, nonatomic) IBOutlet UILabel *lblImportCountStatus; //Label 

上面代码中的一切都按预期工作正常,但问题是 importStatusView 被添加到屏幕但 lblImportCountStatus 值未显示,但如果我记录它显示更新的值。

当枚举结束时,所有的 NSLog 都会被打印出来,例如如果我导入了 10 张照片而不是最后打印的照片,即 dispatch_async(dispatch_get_main_queue() 此函数在枚举过程中根本不起作用。

Label value->1 of 10
Label value->2 of 10
Label value->3 of 10
Label value->4 of 10
Label value->5 of 10
Label value->6 of 10
Label value->7 of 10
Label value->8 of 10
Label value->9 of 10
Label value->10 of 10

可能是什么问题?

更新:

-(void)updateImportCount:(NSDictionary*)info{ //(NSNumber*)current forTotalItems:(NSNumber*)totalCount{
    NSNumber *current = [info objectForKey:@"current"];
    NSNumber *totalCount = [info objectForKey:@"totalCount"];
    _lblImportCountStatus.text = [NSString stringWithFormat:@"%d of %d",[current integerValue],[totalCount integerValue]];
    [_lblImportCountStatus setNeedsDisplay];
    NSLog(@"Updating ui->%@",_lblImportCountStatus.text);
}

上面的函数在主线程上工作并更新,但仍然没有显示标签,它在 NSLog 之后打印

start
Updating ui->1 of 10
Finish
start
Updating ui->2 of 10
Finish
start
Updating ui->3 of 10
Finish
start
Updating ui->4 of 10
Finish
start
Updating ui->5 of 10
Finish
start
Updating ui->6 of 10
Finish
start
Updating ui->7 of 10
Finish
start
Updating ui->8 of 10
Finish
start
Updating ui->9 of 10
Finish
start
Updating ui->10 of 10
Finish

我已经在 this location 上传了项目,请随时提供帮助。

最佳答案

所有这些 block 在主线程上执行(验证这一点的最简单方法是使用NSThread+currentThread 来获取当前线程,和 -isMainThread 来检查它是否是主线程。任何你有代码的地方你想看看它在哪个线程上,做这样的事情:

NSLog( @"enumeration block on main thread: %@", 
       [[NSThread currentThread] isMainThread] ? @"YES" : @"NO" );

我认为问题在于,因为这一切都在主线程上执行,所以您锁定了运行循环,没有给 UI 更新的机会。

解决此问题的正确方法可能是真正在单独的线程上执行此处理(调用代码通过 performSelectorOnMainThread: 更新 UI,因为您现在正在做)。但是,使其工作的快速破解方法是允许运行循环运行。在 updateImportCount: 结束时,执行如下操作:

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];

它不漂亮,但它会起作用。

更新: 在 Cocoa(Mac OS 和 iOS)中有一个 runloop 的概念。应用程序的主线程驱动一个用作事件循环的 NSRunLoop。用户操作(点击等)通过此循环进行处理,定时器、网络连接和其他内容也是如此。查看NSRunLoop Reference有关这方面的更多信息。

在 iOS 中,绘图也发生在运行循环中。因此,当您在 View 上调用 setNeedsDisplay 时,该 View 不会立即重绘。相反,它只是被标记为需要重绘,然后在下一个绘图周期(runloop 的下一个行程)进行实际绘图。 UIView reference对此有一个简短的描述(参见“ View 绘制周期”部分)。引用该部分:

When the actual content of your view changes, it is your responsibility to notify the system that your view needs to be redrawn. You do this by calling your view’s setNeedsDisplay or setNeedsDisplayInRect: method of the view. These methods let the system know that it should update the view during the next drawing cycle. Because it waits until the next drawing cycle to update the view, you can call these methods on multiple views to update them at the same time.

当您从为响应某些用户操作而调用的任何方法返回时(在本例中为 somePicker:didFinishPickingMediaWithInfo:),控制权返回到运行循环,并且任何需要重绘的 View 都是。但是, 如果你在主线程上做了一堆处理没有返回, runloop 基本上停顿了, 绘图不会发生. 上面的代码基本上给了runloop 一些时间来处理, 所以绘图可以发生. [NSDate date] 返回当前日期和时间,就在现在,所以这基本上告诉 runloop “运行到现在”,最终给它一个循环通过循环,给你一个绘图周期,这是重新绘制标签的机会。

关于ios - 从 block 更新 uilabel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22316718/

相关文章:

ios - 即使重新加载后,UITableView 滚动也很缓慢

ios - 为不同的点更改 CPScatterPlot 的线条样式

iphone - Skybox OpenGL ES iPhone和iPad

iphone - 自定义 UINavigationBar 字体不显示

objective-c - 多线程 Objective-C 访问器 : GCD vs locks

ios - 使用一个 Collection View IOS 滚动多个 UICollectionView

ios - NSUserDefaults.standardUserDefaults().stringForKey ("useremail") 值返回 null

ios - UIDocumentPickerViewController 不显示所有文件

ios - AVPlayer上线了吗?

ios - 使用 Restkit 忽略映射中的空数组