为什么我的 UITableView “跳转"插入或删除行时? [英] Why does my UITableView "jump" when inserting or removing a row?

查看:17
本文介绍了为什么我的 UITableView “跳转"插入或删除行时?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(很高兴接受 Swift 或 Objective-C 的答案)

(Happy to accept an answer in Swift or Objective-C)

我的表格视图有几个部分,当按下按钮时,我想在第 0 部分的末尾插入一行.再次按下按钮,我想删除同一行.我几乎可以工作的代码如下所示:

My table view has a few sections, and when a button is pressed, I want to insert a row at the end of section 0. Pressing the button again, I want delete that same row. My almost working code looks like this:

// model is an array of mutable arrays, one for each section

- (void)pressedAddRemove:(id)sender {
    self.adding = !self.adding;  // this is a BOOL property
    self.navigationItem.rightBarButtonItem.title = (self.adding)? @"Remove" : @"Add";

    // if adding, add an object to the end of section 0
    // tell the table view to insert at that index path

    [self.tableView beginUpdates];
    NSMutableArray *sectionArray = self.model[0];
    if (self.adding) {
        NSIndexPath *insertionPath = [NSIndexPath indexPathForRow:sectionArray.count inSection:0];
        [sectionArray addObject:@{}];
        [self.tableView insertRowsAtIndexPaths:@[insertionPath] withRowAnimation:UITableViewRowAnimationAutomatic];

    // if removing, remove the object from the end of section 0
    // tell the table view to remove at that index path

    } else {
        NSIndexPath *removalPath = [NSIndexPath indexPathForRow:sectionArray.count-1 inSection:0];
        [sectionArray removeObject:[sectionArray lastObject]];
        [self.tableView deleteRowsAtIndexPaths:@[removalPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
    [self.tableView endUpdates];
}

这有时会正常运行,但有时不会,这取决于滚动表格视图的位置:

This behaves properly sometimes, but sometimes not, depending on where the table view is scrolled:

  • 最顶部的第 0 部分,contentOffset.y == 0:效果很好,该行已插入,第 0 部分下方的内容向下动画
  • 第 0 部分不可见,因为表格已滚动通过它:效果很好,新行下方的可见内容向下移动,就像在其上方插入了一行一样.
  • 但是:如果 table view 滚动一点,那么第 0 部分的部分是可见的:它工作错误.在单个框架中,表视图中的所有内容都向上跳(内容偏移量增加)然后,通过动画,新行被插入并且表视图内容向下滚动(内容偏移量减少).一切都以应有的方式结束,但在开始时的单帧跳跃"过程中看起来非常糟糕.

我可以通过调试->切换慢速动画"在模拟器的慢动作中看到这种情况.同样的问题在删除时反向出现.

I can see this happen in slow-motion the simulator with "Debug->Toggle Slow Animations". The same problem occurs in reverse on the deletion.

我发现偏移量跳转的大小与表格滚动到第 0 部分的距离有关:当偏移量很小时跳转很小.当滚动接近第 0 部分总高度的 一半 时,跳跃变得更大(这里的问题是最糟糕的,跳跃 == 部分高度的一半).进一步滚动,跳跃变得更小.当表格滚动到只有少量部分 0 仍然可见时,跳转很小.

I've found that the size of the jump in offset is related to the how far into section 0 the table is scrolled: the jump tiny when the offset is tiny. The jump gets bigger as the scrolling approaches half of section 0 total height (the problem is at it's worst here, jump == half the section height). Scrolling further, the jump gets smaller. When the table is scrolled so that only a tiny amount of section 0 is still visible, the jump is tiny.

你能帮我理解为什么会这样以及如何解决吗?

推荐答案

在 iOS 11 上,UITableView 默认使用估计行高.

On iOS 11, UITableView uses estimated row height as default.

在插入/重新加载或删除行时会导致不可预知的行为,因为 UITableView 大部分时间都有错误的内容大小:

It leads to unpredictable behaviors when inserting/reloading or deleting rows because the UITableView has a wrong content size most of the time:

为了避免过多的布局计算,tableView 只为每个 cellForRow 调用询问 heightForRow 并记住它(在正常模式下,tableView 询问 heightForRowcode> 用于 tableView 的所有 indexPaths).其余单元格的高度等于 estimatedRowHeight 值,直到它们对应的 cellForRow 被调用.

To avoid too many layout calculations, the tableView asks heightForRow only for each cellForRow call and remembers it (in normal mode, the tableView asks heightForRow for all the indexPaths of the tableView). The rest of the cells has a height equal to the estimatedRowHeight value until their corresponding cellForRow is called .

// estimatedRowHeight mode
contentSize.height = numberOfRowsNotYetOnScreen * estimatedRowHeight + numberOfRowsDisplayedAtLeastOnce * heightOfRow

// normal mode
contentSize.height = heightOfRow * numberOfCells

我猜因为这个技巧,UIKit 很难正确地为更改设置动画.

I guess UIKit struggles to animate correctly the changes because of this trick.

一种解决方案是禁用estimatedRowHeight 模式,方法是将estimatedRowHeight 设置为0 并为每个单元格实现heightForRow.

One solution is to disable the estimatedRowHeight mode by setting estimatedRowHeight to 0 and implementing heightForRow for each of your cells.

当然,如果您的单元格具有动态高度(大部分时间都需要进行繁重的布局计算,因此您有充分的理由使用 estimatedRowHeight),您就必须找到一种方法来重现 EstimatedRowHeight 在不影响 tableView 的 contentSize 的情况下进行优化.看看 AsyncDisplayKitUITableView-FDTemplateLayoutCell.

Of course, if your cells have dynamic heights (with onerous layout calculations most of time so you used estimatedRowHeight for a good reason), you would have to find a way to reproduce the estimatedRowHeight optimization without compromising the contentSize of your tableView. Take a look at AsyncDisplayKit or UITableView-FDTemplateLayoutCell.

另一个解决方案是尝试找到一个合适的 estimatedRowHeight.从 iOS 10 开始,你也可以尝试使用 UITableView.automaticDimension.UIKit 会为你找到一个值:

Another solution is to try to find a estimatedRowHeight which suits well. Since iOS 10, you can also try to use UITableView.automaticDimension. UIKit will find a value for you:

tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = UITableView.automaticDimension

在 iOS 11 上,它已经是默认值.

On iOS 11, it's already the default value.

这篇关于为什么我的 UITableView “跳转"插入或删除行时?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆