【问题标题】:Update uilabel from blocks从块中更新 uilabel
【发布时间】:2014-03-11 04:49:52
【问题描述】:

从相册中导入多张照片,其中一种委托方法是

// 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]);
            }];
        }];
    }];
}

我对状态视图和标签的声明是

@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,请随时提供帮助。

【问题讨论】:

  • 你确定label连接对了吗,检查nil?
  • 可能在更新 UI 之前,pickerview 已被解除并从内存中释放。
  • @DmitryShevchenko 如果我 nslog 标签值它会打印 n 中的 1、n 中的 2 等等
  • @whitewolf09 AFAIK pickerview 与 UI 更新无关,您能说得更具体些吗?
  • @SatishAzad 框架是 {{40,29},{100,21}},是的,这是我找不到的问题,所以向 GURU 寻求帮助:)。

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


【解决方案1】:

所有这些块 在主线程上执行(验证这一点的最简单方法是使用 NSThread+currentThread 获取当前线程,并使用 -isMainThread 检查它是否主线程。任何你想要查看它所在线程的代码的地方,都可以执行以下操作:

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

我认为问题在于,由于这一切都在主线程上执行,因此您锁定了运行循环,而不是让 UI 有机会更新。

解决此问题的正确方法可能是真正在单独的线程上执行此处理(通过performSelectorOnMainThread: 调用代码以更新UI,就像您现在所做的那样)。但是,让它工作的一个快速技巧是允许 runloop 运行。在updateImportCount: 的末尾,做这样的事情:

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

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

更新: 在 Cocoa(Mac OS 和 iOS)中有一个 runloop 的概念。应用程序的主线程驱动一个用作事件循环的 NSRunLoop。用户操作——点击等——通过这个循环进行处理,就像计时器、网络连接和其他东西一样。有关更多信息,请参阅NSRunLoop Reference

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

当您的视图的实际内容发生变化时,它是您的 有责任通知系统您的视图需要 重绘。您可以通过调用视图的 setNeedsDisplay 或 setNeedsDisplayInRect:视图的方法。这些方法让 系统知道它应该在下一次绘图期间更新视图 循环。因为它要等到下一个绘图周期才更新 视图,您可以在多个视图上调用这些方法来更新它们 同一时间。

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

【讨论】:

  • 这个可行,我目前已经在模拟器上测试过了。你能详细说明它的实际作用吗?为什么它有效?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多