为什么UITableView的reloadData不能在动画块中使用?

3

之前我不知道为什么,当我调用 reloadData 方法时,我的tableView会花费很长时间。

enter image description here

我进行了测试并记录了Log以证明,慢的原因不是网络问题:

2016-12-29 14:50:20.958 Eee[1572:25220] lml-info-vc-test-net-began
2016-12-29 14:50:20.958 Eee[1572:25220] lml-info-vc-test-net-end
2016-12-29 14:50:20.972 Eee[1572:25220] lml-info-vc-test-net-animations-reloadData-bigin
2016-12-29 14:50:34.870 Eee[1572:25220] lml-info-vc-test-net-animations-reloadData-end

观察到,net-begannet-end的时间非常短。
但是,reloadData-biginreloadData-end的时间很长,因此我在SO上搜索了一下,reloadData做了什么?我想深入了解,不要简单的答案,我的搜索结果总是简单的回答,缺乏深入分析。

我的reloadData代码:

//[self.pre_tableView reloadData];
[UIView animateWithDuration:0 animations:^{

     NSLog(@"lml-info-vc-test-net-animations-reloadData-bigin");

     [self.pre_tableView reloadData];
     NSLog(@"lml-info-vc-test-net-animations-reloadData-end");
 } completion:^(BOOL finished) {
     //Do something after that...
     [_pre_tableView.mj_footer endRefreshing];
     [_pre_tableView.mj_header endRefreshing];

 }];

我使用动画块,并在 completionHandler 中结束刷新。
我还查看了苹果的文档,关于 reloadData
在讨论中:
调用此方法以重新加载用于构造表格的所有数据,包括单元格、部分标题和页脚、索引数组等。为了提高效率,表视图只重新显示可见的行。如果表格因重新加载而缩小,则会调整偏移量。当表视图的委托或数据源希望完全重新加载其数据时,它会调用此方法。不应在插入或删除行的方法中调用此方法,特别是在使用 beginUpdates 和 endUpdates 调用实现的动画块内。
注意:
根据上述句子:
“不应在插入或删除行的方法中调用此方法,特别是在使用动画块的情况下。”
这意味着我不应该在我的 UIViewanimateWithDuration 方法中使用 reloadData。因此,我将我的代码替换为以下内容:
[self.pre_tableView reloadData];
[_pre_tableView.mj_footer endRefreshing];
[_pre_tableView.mj_header endRefreshing];

现在不再慢了,我很高兴,找到了原因。

但是,我想知道为什么不能把 reloadData 方法放在动画块中?

而且,即使在动画块中需要很长时间,reloadData 也不会失败,发生了什么?那么它在这里需要这么长时间干什么?


编辑 -1

我的附加代码如下:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return self.pre_dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:info_TableIdentifier];

    if (cell == nil) {

        cell = [[LMLAgricultureTechCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:info_TableIdentifier];
    }
    //NSLog(@"lml-info-vc-test-cellSetModel-began");
    ((LMLAgricultureTechCell *)cell).model = self.pre_dataSource[indexPath.row];
    //NSLog(@"lml-info-vc-test-cellSetModel-end");
    ((LMLAgricultureTechCell *)cell).indexPath = indexPath;
    ((LMLAgricultureTechCell *)cell).delegate = self;
    ((LMLAgricultureTechCell *)cell).photo_view.delegate = self;

    return cell;

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // >>>>>>>>>>>>>>>>>>>>> * cell自适应 * >>>>>>>>>>>>>>>>>>>>>>>>
    id model;

    model = self.pre_dataSource[indexPath.row];

    return [self.pre_tableView cellHeightForIndexPath:indexPath model:model keyPath:@"model" cellClass:[LMLAgricultureTechCell class] contentViewWidth:[self cellContentViewWith]];
}

动画块用于创建动画,为什么要在那里放置reload data?可能是因为它正在尝试在重新渲染单元格时同时对所有内容进行动画处理,导致速度变慢。 - Tj3n
@Tj3n,虽然我通过苹果的文档知道不能在动画块中放置reloadData,但是为什么呢?文档没有做更多的解释。 - aircraft
请展示您对于 UITableViewDataSource 和 ... Delegate 的方法,特别是 cellForRowAtIndexPath。 - Hermann Klecker
你知道根据文档,你不能在动画块中放置reloadData,所以请遵守。 - GeneCode
1个回答

1

UIView.animateWithDuration... 方法只能动画化可动画的值。尽管有时它看起来像魔法-但实际上不是...reloadData 是一个异步方法,无法通过 animate 块进行处理以进行动画处理。

如果您想要动画过渡,可以使用 insert/delete/moveRows 或使用 UIViewtransitionWithView 方法。此方法会将视图完全呈现在屏幕外,就像在其块中放置所有更改后一样,然后在当前视图状态和新呈现视图之间进行动画转换。动画本身取决于您提供的选项,您可能希望使用 UIViewAnimationOptionsTransitionCrossDissolve

[UIView transitionWithView:self.pre_tableView 
              duration:0.3 
               options:UIViewAnimationOptionsTransitionCrossDissolve 
            animations: ^{ 
                [self.pre_tableView reloadData];
            } completion: ^(BOOL finished) { 
                ... 
            }];

正如你所说,“reloadData是一个异步方法,不能被animate block处理并进行动画处理”,但即使它需要很长时间重新加载,它仍然会成功地重新加载,只是需要很长时间。为什么需要这么长时间?而不是失败。 - aircraft

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接