具有动态单元格高度的UITableView的reloadData()导致跳转滚动

Dav*_*vid 124 uitableview ios autolayout swift

我觉得这可能是一个普遍的问题,并且想知道是否有任何共同的解决方案.

基本上,我的UITableView具有每个单元格的动态单元格高度.如果我不在UITableView和我的顶部tableView.reloadData(),向上滚动变得有点跳跃.

我相信这是因为我重新加载数据,因为我正在向上滚动,UITableView会重新计算每个进入可见性的单元格的高度.如何缓解这种情况,或者如何仅将数据从某个IndexPath重新加载到UITableView的末尾?

此外,当我设法一直滚动到顶部时,我可以向下滚动然后向上滚动,没有跳跃没问题.这很可能是因为已经计算了UITableViewCell高度.

Igo*_*gor 187

为了防止跳跃,你应该在加载时保存单元格的高度,并给出准确的值tableView:estimatedHeightForRowAtIndexPath:

// declare cellHeightsDictionary
NSMutableDictionary *cellHeightsDictionary;

// initialize in code (thanks to @Gerharbo)
cellHeightsDictionary = @{}.mutableCopy;

// declare table dynamic row height and create correct constraints in cells
tableView.rowHeight = UITableViewAutomaticDimension;

// save height
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    [cellHeightsDictionary setObject:@(cell.frame.size.height) forKey:indexPath];
}

// give exact height value
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSNumber *height = [cellHeightsDictionary objectForKey:indexPath];
    if (height) return height.doubleValue;
    return UITableViewAutomaticDimension;
}
Run Code Online (Sandbox Code Playgroud)

  • 不要忘记初始化`cellHeightsDictionary`:`cellHeightsDictionary = [NSMutableDictionary dictionary];` (3认同)

Cas*_*ner 104

Swift 3版本接受了答案.

var cellHeights: [IndexPath : CGFloat] = [:]


func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cellHeights[indexPath] = cell.frame.size.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeights[indexPath] ?? 70.0 
}
Run Code Online (Sandbox Code Playgroud)


Kri*_*ore 36

跳跃是因为估计的高度不好.estimatedRowHeight与实际高度的差异越大,表重新加载时表可能跳得越多,尤其是滚动得越远.这是因为表的估计大小与其实际大小完全不同,迫使表调整其内容大小和偏移量.所以估计的高度不应该是随机值,而是接近你认为的高度.我也经历过,UITableViewAutomaticDimension 如果你的细胞是同一类型的话

func viewDidLoad() {
     super.viewDidLoad()
     tableView.estimatedRowHeight = 100//close to your cell height
}
Run Code Online (Sandbox Code Playgroud)

如果你在不同的部分有各种各样的细胞,那么我认为更好的地方是

func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
     //return different sizes for different cells if you need to
     return 100
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这就是为什么我的tableView如此跳动的原因。 (2认同)

Kir*_*nee 25

@Igor的答案在这种情况下工作得很好Swift-4,它的代码.

// declaration & initialization  
var cellHeightsDictionary: [IndexPath: CGFloat] = [:]  
Run Code Online (Sandbox Code Playgroud)

在以下方法中 UITableViewDelegate

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
  // print("Cell height: \(cell.frame.size.height)")
  self.cellHeightsDictionary[indexPath] = cell.frame.size.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
  if let height =  self.cellHeightsDictionary[indexPath] {
    return height
  }
  return UITableViewAutomaticDimension
}
Run Code Online (Sandbox Code Playgroud)

  • 如何使用此解决方案处理行插入/删除?TableView跳转,因为字典数据不是实际的. (6认同)

Sru*_*mha 18

我已经尝试了上面的所有解决方法,但没有任何效果.

在花了几个小时并经历所有可能的挫折之后,找到了解决这个问题的方法.这个解决方案是生命救星!像魅力一样工作!

斯威夫特4

let lastContentOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastContentOffset, animated: false)
Run Code Online (Sandbox Code Playgroud)

我将它添加为扩展,以使代码看起来更干净,并避免每次我想重新加载时写下所有这些行.

extension UITableView {

    func reloadWithoutAnimation() {
        let lastScrollOffset = contentOffset
        beginUpdates()
        endUpdates()
        layer.removeAllAnimations()
        setContentOffset(lastScrollOffset, animated: false)
    }
}
Run Code Online (Sandbox Code Playgroud)

终于..

tableView.reloadWithoutAnimation()
Run Code Online (Sandbox Code Playgroud)


Mar*_*Wan 11

我今天碰到了这个并观察到:

  1. 确实只是iOS 8.
  2. 重叠cellForRowAtIndexPath没有帮助.

修复实际上非常简单:

覆盖estimatedHeightForRowAtIndexPath并确保它返回正确的值.

有了这个,我的UITableViews中所有奇怪的抖动和跳跃都停止了.

注意:我实际上知道我的细胞的大小.只有两个可能的值.如果您的单元格是真正可变大小的,那么您可能希望缓存cell.bounds.size.heightfromtableView:willDisplayCell:forRowAtIndexPath:

  • 修复了使用高值(例如300f)覆盖estimatedHeightForRowAtIndexPath方法的问题 (2认同)

ras*_*slv 10

我使用更多的方法来解决它:

对于视图控制器:

var cellHeights: [IndexPath : CGFloat] = [:]


func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cellHeights[indexPath] = cell.frame.size.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeights[indexPath] ?? 70.0 
}
Run Code Online (Sandbox Code Playgroud)

作为UITableView的扩展

extension UITableView {
  func reloadSectionWithouAnimation(section: Int) {
      UIView.performWithoutAnimation {
          let offset = self.contentOffset
          self.reloadSections(IndexSet(integer: section), with: .none)
          self.contentOffset = offset
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

结果是

tableView.reloadSectionWithouAnimation(section: indexPath.section)
Run Code Online (Sandbox Code Playgroud)


Lyn*_*ott 8

实际上,您可以使用reloadRowsAtIndexPaths,例如重新加载某些行:

tableView.reloadRowsAtIndexPaths(indexPathArray, withRowAnimation: UITableViewRowAnimation.None)
Run Code Online (Sandbox Code Playgroud)

但是,通常,您还可以为表格单元格高度更改设置动画,如下所示:

tableView.beginUpdates()
tableView.endUpdates()
Run Code Online (Sandbox Code Playgroud)