如何显示UIActivityIndi​​catorView之前的旋转开始

我想在willAnimateRotationToInterfaceOrientation进行的工作之前显示活动指示器:duration:开始。 大多数情况下,在我的应用程序中,这项工作很快完成,不需要任何活动指示器,但偶尔(第一次轮换,即在caching数据之前,处理大文件时)可能会出现明显的延迟。 而不是重新构build我的应用程序来应付这种罕见的情况下,我宁愿只显示UIActivityIndi​​catorView而应用程序生成caching并更新显示。

问题是(或者似乎是)在willRotateToInterfaceOrientation:duration和willAnimateRotationToInterfaceOrientation:duration:方法之间没有更新显示。 所以要求iOS在willRotate方法中显示UIActivityIndi​​cator视图直到在willAnimateRotation方法之后才会实际影响显示。

以下代码说明了这个问题。 运行时,活动指示器只会非常短暂地出现,并且在完成simulateHardWorkNeededToGetDisplayInShapeBeforeRotation方法之后。

我错过了什么明显的? 如果没有,我怎么能解决这个问题有什么聪明的想法?

更新 :虽然关于种植繁殖到另一个线程等的build议通常是有帮助的,在我的具体情况下,我有一些确实想要阻止主线程来做我的提升。 在应用程序中,我有一个tableView所有的高度需要重新计算。 当 – 这不是一个很常见的用例,或者我甚至不会考虑这种方法 – 有很多行,所有的新高度在[tableView reloadData]中计算(然后caching)。 如果农场的起飞,并让旋转继续,那么在旋转之后,提升之前,我的tableView没有被重新加载。 例如,在横向的情况下,它不占用全部的宽度。 当然,还有其他的解决方法,例如在旋转之前创build一个只有几行的tableView,然后重新加载真正的tableView。

示例代码来说明问题:

@implementation ActivityIndicatorViewController @synthesize activityIndicatorView = _pgActivityIndicatorView; @synthesize label = _pgLabel; - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); } - (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration; { NSLog(@"willRotate"); [self showActivityIndicatorView]; } - (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration; { NSLog(@"willAnimateRotation"); [self simulateHardWorkNeededToGetDisplayInShapeBeforeRotation]; } - (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation; { NSLog(@"didRotate"); [self hideActivityIndicatorView]; } - (void) simulateHardWorkNeededToGetDisplayInShapeBeforeRotation; { NSLog(@"Starting simulated work"); NSDate* date = [NSDate date]; while (fabs([date timeIntervalSinceNow]) < 2.0) { // } NSLog(@"Finished simulated work"); } - (void) showActivityIndicatorView; { NSLog(@"showActivity"); if (![self activityIndicatorView]) { UIActivityIndicatorView* activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; [self setActivityIndicatorView:activityIndicatorView]; [[self activityIndicatorView] setCenter:[[self view] center]]; [[self activityIndicatorView] startAnimating]; [[self view] addSubview: [self activityIndicatorView]]; } // in shipping code, an animation with delay would be used to ensure no indicator would show in the good cases [[self activityIndicatorView] setHidden:NO]; } - (void) hideActivityIndicatorView; { NSLog(@"hideActivity"); [[self activityIndicatorView] setHidden:YES]; } - (void) dealloc; { [_pgActivityIndicatorView release]; [super dealloc]; } - (void) viewDidLoad; { UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(50.0, 50.0, 0.0, 0.0)]; [label setText:@"Activity Indicator and Rotate"]; [label setTextAlignment: UITextAlignmentCenter]; [label sizeToFit]; [[self view] addSubview:label]; [self setLabel:label]; [label release]; } @end 

该应用程序不会更新屏幕以显示UIActivityIndicatorView直到主运行循环重新获得控制。 当发生旋转事件时,将通过主运行循环一次调用willRotate...willAnimateRotation...方法。 因此,在显示活动指示符之前,请先阻止艰苦的工作方法。

为了使这个工作,你需要把努力工作推到另一个线程。 我会把这个调用放在willRotate...方法中的辛勤工作方法。 该方法会在工作完成时callback给该视图控制器,以便更新视图。 我会放在willAnimateRotation...方法中显示活动指标。 我不会打扰一个didRotateFrom...方法。 我build议阅读“ 线程编程指南” 。

编辑回应评论:您可以通过使willAnimateRotation...方法在屏幕上放置非function性界面(如显示黑色叠加层的视图和UIActivityIndicatorView来有效地阻止用户交互。 然后,当繁重的工作完成后,这个覆盖被删除,接口再次变为活动。 然后,绘图代码将有机会正确添加和animation活动指标。

更多的挖掘(首先在马特Neuberg的编程iPhone 4),然后这迫使核心animation运行它的线程从stackoverflow这个有帮助的问题 ,我有一个解决scheme,似乎运作良好。 Neuberg和苹果都对这种方法非常谨慎,因为可能会产生不受欢迎的副作用。 在目前的testing中,对于我的特殊情况来说似乎没有问题。

更改上面的代码如下执行更改。 添加的关键是[CATransaction flush] ,强制UIActivityIndi​​catorView开始显示,即使在willAnimateRotationToInterfaceOrientation:duration方法完成之后,运行循环不会结束。

 - (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration; { NSLog(@"willRotate"); [self showActivityIndicatorView]; [CATransaction flush]; // this starts the animation right away, w/o waiting for end of the run loop } - (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration; { NSLog(@"willAnimateRotation"); [self simulateHardWorkNeededToGetDisplayInShapeBeforeRotation]; [self hideActivityIndicatorView]; } - (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation; { NSLog(@"didRotate"); } 

显示活动视图后,尝试执行第二个线程的工作。

 [self showActivityIndicatorView]; [self performSelector:@selector(simulateHardWorkNeededToGetDisplayInShapeBeforeRotation) withObject:nil afterDelay:0.01]; 

要么在后台线程中执行繁重的工作,并在前台线程中发布结果以更新UI(从iOS 4.0开始,UIKit只是线程安全的):

 [self performSelectorInBackground:@selector(simulateHardWorkNeededToGetDisplayInShapeBeforeRotation) withObject:nil] 

或者您可以安排旋转发生之后执行繁重的提升方法:

 [self performSelector:@selector(simulateHardWorkNeededToGetDisplayInShapeBeforeRotation) withObject:nil afterDelay:0.4] 

但这些只是黑客,真正的解决办法是有适当的后台处理,如果你的用户界面需要大量的处理来更新,可能是在肖像或风景。 NSOperationNSOperationQueue是一个很好的起点。