从不是主线程的线程更改GUI?

时间:2010-03-06 14:03:15

标签: iphone cocoa cocoa-touch multithreading

我的想法是在幕后计算某些内容时显示用户的视图。这是一个高度为30px的视图,其中包含一个UIActivityIndi​​catorView和一个显示正在计算的内容的UILabel。

我试图通过使用这些方法的ActivityHandler来实现它:

- (void)newActivityStarted{
    [self performSelectorOnMainThread:@selector(showActivityViewer) withObject:nil waitUntilDone:NO];
}

- (void)activityStopped{
    [self performSelectorOnMainThread:@selector(hideActivityViewer) withObject:nil waitUntilDone:NO];
}

- (void)changeInfoText:(NSString*)infoText{
    [activityView.infoLabel performSelectorOnMainThread:@selector(setText:) withObject:infoText waitUntilDone:NO];
}

以下是在主线程上调用的方法:

-(void)hideActivityViewer{
    CGContextRef context = UIGraphicsGetCurrentContext();
    [UIView beginAnimations:nil context:context];

    [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
    [UIView setAnimationDuration:0.2];
    [UIView setAnimationDelegate: self];
    [UIView setAnimationDidStopSelector:@selector(closeActivityViewer:finished:context:)];
    [activityView setFrame:CGRectMake(320, 10, 320, 30)];

    [UIView commitAnimations];
}

-(void)closeActivityViewer:(NSString*)id finished:(BOOL)finished context:(id)context{

    [activityView removeFromSuperview];
    [activityView release];
    activityView = nil;
}

-(void)showActivityViewer{

    activityView = [[BCActivityView alloc] initWithFrame:CGRectMake(320, 10, 320, 30)];
    [activityView.infoLabel setText:NSLocalizedString(@"WorkInProgressKey",nil)];

    [[(MyAppDelegate*)[[UIApplication sharedApplication] delegate] window] addSubview: activityView];

    CGContextRef context = UIGraphicsGetCurrentContext();
    [UIView beginAnimations:nil context:context];

    [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
    [UIView setAnimationDuration:0.2];
    [activityView setFrame:CGRectMake(0, 10, 320, 30)];

    [UIView commitAnimations];
}

newActivityStarted,activityStopped,changeInfoText方法由计算一些耗时数据的后台线程调用。

这个耗时的线程会像这样打开:

NSInvocationOperation* operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(calculateData) object:nil];
[[[NSOperationQueue new] autorelease] addOperation:operation];
[operation release];

计算如下:

-(void) calculateData{
    [[ActivityIndicatorHandler instance] newActivityStarted];
    [[ActivityIndicatorHandler instance] changeInfoText:NSLocalizedString(@"InitializingForecastKey", nil)];

    //Do something time consuming

    [[ActivityIndicatorHandler instance] activityStopped];
}

效果是,在整个计算完成后显示并隐藏活动视图。我希望GUI能够在后台完成计算时显示视图并对用户交互负责。但即使我在计算中有断点,在计算线程完成其工作之前视图也不可用,并且活动视图未显示......我无法弄清楚出了什么问题... < / p>

有人有想法吗?

3 个答案:

答案 0 :(得分:2)

@implementation ActivityHandler;

//....
- (void)newActivityStarted{
    [self performSelectorOnMainThread:@selector(showActivityViewer) withObject:nil waitUntilDone:NO];
}

- (void)activityStopped{
    [self performSelectorOnMainThread:@selector(hideActivityViewer) withObject:nil waitUntilDone:NO];
}

- (void)changeInfoText:(NSString*)infoText{
    [activityView.infoLabel performSelectorOnMainThread:@selector(setText:) withObject:infoText waitUntilDone:NO];
}


- (NSInvocationOperation*)myOperation:
{
    NSInvocationOperation* operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(calculateData) object:nil] autorelease];
    return operation;
}

在某些其他方法中,可能是您的应用委托,在初始值设定项中设置了本地队列:

NSOperaionQueue* aQueue = [[[NSOperationQueue alloc] init] retain];  //release in dealloc
ActivityHandler* myHandler [[[ActivityHandler alloc] init] retain];  //release in dealloc

Have a method to add ops to the queue:

-(void)queueOperation:(NSInvocationOperation*)anOp;
{
    [aQueue addOperation:anOp];
}

-(IBAction*)startCalcs:(id)sender;
{
    [self queueOperation:[myHandler myOperation]];

}

答案 1 :(得分:0)

您是否尝试通过执行此操作来调用计算:

[self performSelectorInBackground:@selector(calculateData) withObject:nil];

答案 2 :(得分:0)

我猜你需要考虑performSelectorOnMainThread:withObject:waitUntilDone:method