ios:UIAlertView在循环执行之前立即显示

时间:2013-06-25 19:39:30

标签: ios uialertview

我有以下代码:

 UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Loading Content For the First Time..."
                                                   message:@"\n"
                                                  delegate:self
                                         cancelButtonTitle:nil
                                         otherButtonTitles:nil];

            UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
            spinner.center = CGPointMake(139.5, 75.5); // .5 so it doesn't blur
            [alertView addSubview:spinner];
            [spinner startAnimating];
            [alertView show];

            for (TCMLevelRemote *level in [obj objectForKey:@"levels"]){
                [[TCMExhibitFeedStore sharedStore] createLevel:level];
            }
            [[TCMExhibitFeedStore sharedStore] loadAllLevels];
            [[TCMExhibitFeedStore sharedStore] setAllLevels:[[TCMExhibitFeedStore sharedStore] storedLevels]];
            [alertView dismissWithClickedButtonIndex:0 animated:YES];

for循环需要一段时间才能执行,因为它会在应用首次运行时下载一些信息。因此,我希望此通知显示,以便用户不会坐在无响应的屏幕上等待。问题是在for循环结束之前,alertview不会显示。然后它马上就消失了。我需要改变什么?

3 个答案:

答案 0 :(得分:11)

.h 类中声明您的alert-view对象,以便在 .m 类中的任何位置使用。

将您的for循环代码放在performSelectorInBackground中,以便在Backgroud中运行循环,这样您就可以不等待ForLoop的完成了。

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

-(void)LoadForLoop
{
 for (TCMLevelRemote *level in [obj objectForKey:@"levels"]){
                [[TCMExhibitFeedStore sharedStore] createLevel:level];
            }
            [[TCMExhibitFeedStore sharedStore] loadAllLevels];
            [[TCMExhibitFeedStore sharedStore] setAllLevels:[[TCMExhibitFeedStore sharedStore] storedLevels]];
            [alertView dismissWithClickedButtonIndex:0 animated:YES];

}

其他解决方案

您也可以按照代码使用Grand Central Dispatch (GCD),如下所示: -

UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Loading Content For the First Time..."
                                                   message:@"\n"
                                                  delegate:self
                                         cancelButtonTitle:nil
                                         otherButtonTitles:nil];

            UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
            spinner.center = CGPointMake(139.5, 75.5); // .5 so it doesn't blur
            [alertView addSubview:spinner];
            [spinner startAnimating];
            [alertView show];



dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          for (TCMLevelRemote *level in [obj objectForKey:@"levels"]){
                [[TCMExhibitFeedStore sharedStore] createLevel:level];
            }
            [[TCMExhibitFeedStore sharedStore] loadAllLevels];
            [[TCMExhibitFeedStore sharedStore] setAllLevels:[[TCMExhibitFeedStore sharedStore] storedLevels]];

        dispatch_async(dispatch_get_main_queue(), ^{
            [spinner StopAnimating];
            [alertView dismissWithClickedButtonIndex:0 animated:YES];
        });
    });

答案 1 :(得分:1)

我认为您正在寻找的是在警报视图向用户显示活动指示器时创建“级别”。

现在,您在与UI代码相同的线程上运行for循环。您的代码将按顺序,一行一行地运行。在iOS和Mac OS上,必须为线程的run loop提供空间以允许渲染和计时事件,在本例中为动画。通过阻止运行​​循环直到for循环结束,您的UIAlertView将没有时间在循环之后设置动画,然后您的dismissWithClickedButtonIndex:animated:调用会立即隐藏它。

您要做的是将处理转移到后台,使用Grand Central Dispatch

// Perform all of your UI on the main thread:
// ... set up your alert views, etc

// Then shift your logic to a background thread:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    // This block is executed on a background thread, so will not block the UI:
    for (TCMLevelRemote *level in [obj objectForKey:@"levels"]){
        [[TCMExhibitFeedStore sharedStore] createLevel:level];
    }
    [[TCMExhibitFeedStore sharedStore] loadAllLevels];
    [[TCMExhibitFeedStore sharedStore] setAllLevels:[[TCMExhibitFeedStore sharedStore] storedLevels]];

    // Finally, now that your background process is complete, you can update the interface accordingly by dismissing the alert view:
    dispatch_async(dispatch_get_main_queue(), ^{
        [alertView dismissWithClickedButtonIndex:0 animated:YES];
    });
});

在处理后台线程时,务必注意必须在主线程上执行UI事件

我喜欢将我的任务打包到NSOperation子类中,这有助于将UI与模型逻辑分开,也可以为我处理GCD。我会把它作为锻炼给你。

关于您选择UI的附注:警报视图并非用于通知用户某些进程。它们用于警告用户发生单个事件 。我建议使用像MBProgressHUD这样的东西,特别是因为它内置了对doSomethingInBackgroundWithProgressCallback:completionCallback:的GCD方法的支持。

答案 2 :(得分:0)

不显示警报视图,因为您的循环阻止了主线程,这是必须绘制警报视图的线程。

在您最初拥有代码的地方,请写下:

// Your original code that creates and sets up the alertView
UIAlertView* alertView = ...

// Add this snippet
NSTimeInterval delay = 0.1;  // arbitrary small delay
[self performSelector:@selector(delayedLoop:) withObject:alertView afterDelay:delay];

// Show the alert. Because the delayedLoop: method is invoked
// "a little bit later", the main thread now should be able to
// display your alert view
[alertView show];

将此方法添加到您的班级:

- (void) delayedLoop:(UIAlertView*)alertView
{
  // Add your code that runs the loop and dismisses the alert view
}

此解决方案有点“hackish”,但由于您的循环仍在主线程上下文中运行,因此您不会遇到任何线程问题。如果您愿意在辅助线程中执行循环,那么您应该看看Nithin Gohel的答案。