处理UIActivityIndi​​cator和多线程的最佳方法是什么?

时间:2010-11-22 16:13:11

标签: iphone nsthread uiactivityindicatorview

我一直试图玩这个很长一段时间,我似乎无法找到最好的方法。我很困惑,因为似乎对如何完成这个看似简单的任务有不同的答案/意见。

我希望能够拥有一个名为ActivityIndi​​catorController的可重用类。该控制器有两个主要方法:activateIndicator和deactivateIndicator。它需要UIView作为参数/属性以及标签的NSString。激活后,它将关闭UIView中的用户交互并添加矩形子视图(带有alpha和圆角),UIActivityIndi​​cator控件和状态文本的UILabel。这是可取的,因为这样我不必在每个视图控制器中都有自定义的UIActivityIndi​​catorView代码,或者必须在每个NIB中设置一个ActivityIndi​​cator。

我从根本上解决的问题是如何开始添加和动画ActivityIndi​​cator的过程。我尝试过的一些方法根本不显示新视图。其他人工作,但ActivityIndi​​cator没有动画。

我已经尝试在activateIndicator方法中使用[NSThread detachNewThreadSelector:@selector(startAnimating)toTarget:activityIndi​​cator withObject:nil],但是没有显示新的UIView。

我尝试从调用方法使用[NSThread detachNewThreadSelector:@selector(activateIndicator)toTarget:activityIndi​​catorController withObject:nil],但这会将新UIView的整个创建放在一个单独的线程中。

现在问题:

第1部分:我知道所有的UI都应该在主线程上处理,这是正确的吗?

第2部分:使用[NSThread detachThreadSelector]与NSOperation相比有什么区别/优势/劣势?

第3部分:是否更好:

(a)将冗长的操作发送到新的后台线程,并回调主线程OR

(b)将UIActivityIndi​​catorView startAnimating方法发送到一个单独的线程并在主线程上运行冗长的进程

为什么?

这是我目前的代码:

ActivityViewController类:

-(void)activateIndicator {
NSLog(@"activateIndicator called");
if (isActivated || !delegateView)
    return;
NSLog(@"activateIndicator started");

[delegateView.view setUserInteractionEnabled:NO];
[delegateView.navigationController.view setUserInteractionEnabled:NO];
[delegateView.tabBarController.view setUserInteractionEnabled:NO];

float w = [[UIScreen mainScreen] bounds].size.width;
float h = [[UIScreen mainScreen] bounds].size.height;

NSLog(@"Width = %f\nHeight = %f", w, h);

if (!disabledView) {
    disabledView = [[[UIView alloc] initWithFrame:CGRectMake((w - kNormalWidth) / 2.0, (h - kNormalHeight) / 2.0, kNormalWidth, kNormalHeight)] autorelease];
    disabledView.center = [[[delegateView.view superview] superview] center];
    [disabledView setBackgroundColor:[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.85]];

CALayer *layer = [disabledView layer];
NSLog(@"layer=%@",layer);
NSLog(@"delegate=%@",[layer delegate]);
layer.cornerRadius = 12.0f;
}

if (!activityIndicator) {
    activityIndicator = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(kNormalWidth / 2, 10.0f, 40.0f, 40.0f)] autorelease];
    [activityIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
    activityIndicator.center = disabledView.center;
}

if (!activityLabel) {
    activityLabel = [[[UILabel alloc] initWithFrame:CGRectMake(10.0f, 100.0f, kNormalWidth - 20, 38)] autorelease];
    activityLabel.text = labelText;
    activityLabel.textAlignment = UITextAlignmentCenter;
    activityLabel.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.0f];
    activityLabel.textColor = [UIColor colorWithWhite:1.0f alpha:1.0f];
    activityLabel.center = disabledView.center;
}

[[[delegateView.view superview] superview] addSubview:disabledView];

[[[delegateView.view superview] superview] addSubview:activityIndicator];
[[[delegateView.view superview] superview] addSubview:activityLabel];


[NSThread detachNewThreadSelector:@selector(startAnimating) toTarget:activityIndicator withObject:nil];
}

从应用中的多个位置调用代码:

    ActivityIndicatorController *aic = [[ActivityIndicatorController alloc] init];
aic.delegateView = self;
aic.labelText = @"Test...";
[aic activateIndicator];

//DO LENGTHY WORK ON MAIN THREAD

[aic deactivateIndicator];
[aic release], aic = nil;

3 个答案:

答案 0 :(得分:2)

  

第1部分:我知道所有的UI都应该在主线程上处理,这是正确的吗?

正确。

  

第2部分:使用[NSThread detachThreadSelector]与NSOperation相比有什么区别/优势/劣势?

NSOperation是一个更高级别的界面,允许您对操作进行排队,创建几个相互依赖的操作等。在后台处理任务的其他选项是performSelectorOnMainThread:... / {{ 1}}和Grand Central Dispatch。

  

第3部分:是否更好:

     

(a)将冗长的操作发送到新的后台线程,并回调主线程OR

     

(b)将UIActivityIndi​​catorView startAnimating方法发送到一个单独的线程并在主线程上运行冗长的进程

由于对问题1的回答,(a)是您唯一的选择。

答案 1 :(得分:1)

将您的冗长工作放在一个单独的线程中,这样就不会完全阻止UI,以防您需要进行某些交互(比如取消操作)。然后,您的ActivityIndi​​catorController应调用主线程来执行所有UI操作,例如:

@implementation ActivityIndicatorController

    - (void)activateIndicator
    {
        [self performSelectorOnMainThread:@selector(activateOnMainThread)
                               withObject:nil
                            waitUntilDone:YES];
    }

    - (void)activateOnMainThread
    {
        // Do your actual UI stuff here.
    }

    // And similarly for the deactivate method.

答案 2 :(得分:0)

一旦显示和动画,活动指示器将继续动画,即使主线程被阻止。

但是,视图没有机会出现,因为运行循环尚未执行,因为等待很长时间的操作。

所以我认为你所需要的只是performSelector:withObject:afterDelay,延迟时间为0,所以你的冗长操作将在你的指示器可见后排队并执行。