1

我有以下代码:

 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 循环结束才显示警报视图。然后它马上就消失了。我需要改变什么?

4

3 回答 3

11

在.h类中声明您的alert-view对象,以便在.m类中随处使用。

将您的 for 循环代码放入 performSelectorInBackgroundBackgroud 中的 for running 循环中,这样您的 Alertview 就不会等待您的 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];
        });
    });
于 2013-06-25T19:49:08.603 回答
1

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

现在,您正在for与 UI 代码相同的线程上运行循环。您的代码将逐行按顺序运行。在 iOS 和 Mac OS 上,必须给线程的运行循环留出空间以允许渲染和计时事件,在这种情况下是动画。通过阻塞运行循环直到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的东西,特别是因为它内置了对 GCD 方法的支持doSomethingInBackgroundWithProgressCallback:completionCallback:

于 2013-06-25T19:57:59.197 回答
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 的答案。

于 2013-06-25T19:55:26.477 回答