【问题标题】:iPhone activity indicator being delayed?iPhone活动指示器被延迟?
【发布时间】:2011-08-19 18:49:54
【问题描述】:

因此,当我在我的 mapView 中单击标注附件时,几秒钟内没有任何反应,因为它正在发出 url 请求并对其进行解析,所以我想显示活动指示器,这样用户就不会认为它被冻结了。代码如下:

- (void)mapView:(MKMapView *)mv annotationView:(MKAnnotationView *)pin calloutAccessoryControlTapped:(UIControl *)control {
    // start activity indicator 
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    NSLog(@"tapped");

    ArtPiece *artPiece = (ArtPiece *)pin.annotation;

    //when annotation is tapped switches page to the art description page
    artDescription *artD = [[artDescription alloc] initWithNibName:@"artDescription" bundle:nil];
    artD.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
    artD.startingLocation = mapView.userLocation.location.coordinate;
    artD.selectedArtPiece = artPiece;
    NSLog(@"0");
    [self presentModalViewController:artD animated:YES];
    NSLog(@"1");

    [artD loadArt:artPiece];
    NSLog(@"2");
    // stop activity indicator 
    //[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

    [artD release];
}

奇怪的是(对我来说,也许我缺少一些明显的东西,因为我很缺乏经验),直到方法完成后活动指示器才会显示,并且模态视图开始在视图中显示动画。我把 NSLogs 放进去看看什么需要时间。我在“0”和“1”之间停顿了大约 2 秒,在“1”和“2”之间又停了几秒钟。然后指标终于显示出来了,所以我确信它出于某种原因一直等到方法结束。任何想法为什么?

【问题讨论】:

    标签: iphone objective-c cocoa-touch uiactivityindicatorview


    【解决方案1】:

    对 UI 的更改(显示活动指示器)在控制权返回到应用程序的 main run loop 后才会生效。直到您的方法结束并且堆栈展开后才会发生这种情况。您需要显示活动指示器,然后将您正在等待的活动转储到后台线程:

    [self performSelectorInBackground:@selector(doThingINeedToWaitFor:)
                           withObject:anObject];
    

    (注意 Apple recommendsmove away from using threads explicitly; performSelectorInBackground:withObject: 是让一些代码从主线程运行的最简单方法。更复杂的选项可用于其他情况。请参阅Concurrency Programming Guide。)

    重要的问题是UI更新仍然需要在主线程上处理,所以在那个方法中,当工作完成时,你需要回调来停止活动指示器:

    - (void) doThingINeedToWaitFor: (id)anObject {
        // Creating an autorelease pool should be the first thing
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
        // Do your work...
        // ...
        // Update the UI back on the main thread
        [self performSelectorOnMainThread:@selector(allDoneWaiting:)
                               withObject:nil
                            waitUntilDone:YES];
        // Clear out the pool as the final action on the thread
        [pool drain];
    }
    

    在您的回调方法中,您再次隐藏活动指示器并执行任何其他必要的后处理。

    【讨论】:

    • 那么 allDoneWaiting 会是另一种刚刚完成的方法:[UIApplication sharedApplication].networkActivityIndi​​catorVisible = YES; ?
    • 它可能会根据您的需要做其他事情,但基本上,是的。任何涉及 GUI 的代码都需要在主线程上运行。
    • 好的,感谢您的帮助,还有一个问题:它正在崩溃并抱怨自动释放池,我是否需要围绕 doThingINeedToWaitFor 方法中正在执行的操作进行处理?
    • 是的,新线程需要在主函数中设置自己的自动释放池。
    • 谢谢,现在似乎一切正常,我已经检查了你的最佳答案,但是......由于所有这些都在后台进行,带有标注的 mapView 不是被阻止,因此用户可以选择另一个注释并在第一个注释仍在工作时单击其附件按钮。有没有办法在标注点击后阻止 UI,使活动指示器仍按应有的方式更新,但用户在完成之前仍无法交互?
    【解决方案2】:

    您不能在同一个函数中启动和停止活动指示器。

    查看我为这个问题提供的答案:How show activity-indicator when press button for upload next view or webview?

    为清楚起见进行编辑:

    - (void) someFunction
    {
        [activityIndicator startAnimation];
    
        // do computations ....
    
        [activityIndicator stopAnimation];  
    }
    

    上面的代码将不起作用,因为当您在当前运行的函数中包含 activityIndi​​cator 时,您没有给 UI 更新时间。所以我和其他许多人所做的就是把它分解成一个单独的线程,如下所示:

    - (void) yourMainFunction {
        activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    
        [NSThread detachNewThreadSelector:@selector(threadStartAnimating) toTarget:self withObject:nil];
    
        //Your computations
    
        [activityIndicator stopAnimating];
    
    }
    
    - (void) threadStartAnimating {
        [activityIndicator startAnimating];
    }
    

    【讨论】:

    • UI 更改需要在主线程中进行。 Activity 指示器需要在yourMainFunction 中启动动画,需要“分离”一个线程(GCD,NSOperation,NSThread,POSIX)进行计算,然后回调主线程停止动画。
    【解决方案3】:

    有些东西让你的微调器变慢了。我建议使用线程在后台进行繁重的工作。试试这个:

    -(void)myMethod{
        [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
        [NSThread detachNewThreadSelector:@selector(startWorkingThread) toTarget:self withObject:nil];
    }
    
    -(void)startWorkingThread{
         //Heavy lifting
         [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    }
    

    我假设你已经评论了:

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    

    出于测试目的...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多