动画视图控制器并同时在表格视图中重新加载数据

Posted

技术标签:

【中文标题】动画视图控制器并同时在表格视图中重新加载数据【英文标题】:Animate view controller and reload data in table view at the same time 【发布时间】:2015-02-13 10:29:02 【问题描述】:

我有一个根视图控制器,它以标准动画呈现模态视图控制器(模态视图控制器从下到上出现)。

让我们将此视图控制器命名为 MyRootViewControllerMyModalTableViewController

问题是如果MyModalTableViewController在出现时重新加载数据,动画就会停止。

例如:

- (void)openModalViewController 
  MyModalTableViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:@"myModalScreen"];
  [self presentViewController:vc animated:YES];

MyModalTableViewController 我有下一个代码:

- (void)viewDidLoad 
  self.itemList = [[MyData sharedInstance] itemList]; // self.itemList is NSArray


// ...

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
  MyTableViewCell * cell = [tableView dequeReusableCellWithIdentifier:@"myCell"];
  cell.item = self.itemList[indexPath.row];
  return cell;

所以当MyModalTableViewController 从情节提要加载时,它会加载itemList 并在UITableView 上显示它。只有当UITableView 完成加载数据时,演示动画才会开始。我猜是因为动画和数据重新加载在同一个线程中工作。因此,如果我要显示 10000 个项目,则需要几秒钟,然后才会开始演示动画。

太慢了。所以我的问题是解决这个问题的最佳方法是什么?

【问题讨论】:

为什么不反其道而行之?首先是动画(可能带有活动指示器),然后是 reloadData。 【参考方案1】:

嗯,你可以在后台线程中加载你的项目列表

- (void)viewDidLoad 

    //background thread
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^

        //load data
        self.itemList = [[MyData sharedInstance] itemList]; // self.itemList is NSArray

        //main thread
        dispatch_async(dispatch_get_main_queue(), ^
            //reload table
            [self.tableView reloadData];
        );

    );


【讨论】:

是的,你是对的,但它不能解决问题,因为[self.tableView reloadData] 将在动画进行时启动:)【参考方案2】:

MyModalTableViewController中,添加一个方法loadData

- (void)loadData 
     self.itemList = [[MyData sharedInstance] itemList]; // self.itemList is NSArray
     [self.tableView reloadData];
 

然后使用`presentViewController的完成块

 [self presentViewController:vc animated:YES completion:^
    [vc loadData];

【讨论】:

感谢您的回复。我已经考虑过这一点,但我想还有更优雅的解决方案。 另外,如果我先将MyModalTableViewController 推送到UINavigationController,代码会更复杂且不可重用。所以,我将不得不通过导航控制器访问必要的屏幕。 那么viewDidAppear 呢?此方法在动画完成时调用,因此您将获得相同的结果并与导航堆栈兼容。为确保您的代码不会被多次调用(viewDidAppear 在子视图控制器被解除时也会被调用),请使用中间属性来了解它是否是第一个 init。 再次感谢您尝试帮助我。问题不在我预期的地方,它有点深。我已经找到答案了,我稍后会发布。 我很期待看到它;)【参考方案3】:

由于我试图从 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; 委托方法呈现视图控制器而导致的问题。

这个委托方法不在主线程中调用,这就是动画运行缓慢的原因。它看起来太奇怪了,因为在 ios 7 中我没有这样的问题。它仅在 iOS 8 及更高版本上发生。

我在这个 SO 主题中发现了同样的问题:Slow presentViewController performance

所以解决方案是实现如下委托:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
  __block UIViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:@"myVC"];
  dispatch_async(dispatch_get_main_queue(), ^
    [self presentViewController:vc animated:YES completion:nil];
  );

我查看了 Apple 文档,但没有发现此委托方法调用不在主线程中的通知:https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UITableViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:didSelectRowAtIndexPath:

如果有人解释为什么这个问题只在 iOS 8 及更高版本上引起,那就太好了。

【讨论】:

嗨,我现在才看到你的回答,你猜怎么着:几个月前我也遇到了同样的问题!实际上,我正在使用 iOS 8 SDK 在 Swift 中重新编写一个 objC 应用程序,当我尝试从 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 推送一个 viewController 时,动画真的很慢。我认为这是一个 Swift 问题,但多亏了你,我现在知道这是一个 iOS 8 问题。很奇怪,它没有在任何地方提及。【参考方案4】:

可以在之前的视图控制器中对itemlist进行收费,并设置为新的:

- (void)openModalViewController 
  MyModalTableViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:@"myModalScreen"];
  vc.itemlist = self.itemlist;
  [self presentViewController:vc animated:YES];

【讨论】:

以上是关于动画视图控制器并同时在表格视图中重新加载数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在后退按钮上重新加载上一个视图控制器?

在表格视图中重新加载数据而不损害动画

如何快速从另一个视图控制器重新加载表格视图

用动画重新加载子视图

使用标签栏控制器离开视图时如何关闭视图

IOS表视图中的数据加载问题