iOS 8自动单元格高度 – 无法滚动到最后一行

我正在使用iOS 8新的自动resize的单元格。 在视觉上它运作良好 – 每个细胞都有合适的尺寸。 但是,如果我尝试滚动到最后一行 ,表视图似乎不知道它的正确大小。 这是一个错误还是有一个修复?

以下是重新创build问题的方法:

使用这个项目 – TableViewCellWithAutoLayoutiOS8 (从这个SO答复引用),我得到了预期的自动resize的单元格。

但是,如果我正在调用scrollToRowAtIndexPath函数,如下所示:

tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true) 

没有走到最后一排 – 它只是让我到了那里。

即使通过尝试使用这样一个较低级别的function:

 tableView.setContentOffset(CGPointMake(0, tableView.contentSize.height - tableView.frame.size.height), animated: true) 

结果并不如预期,它不会走到尽头。 如果我点击了很多次,或者稍等片刻,最终它会到达正确的位置。 看来tableView.contentSize.height设置不正确,所以iOS“不知道”最后一个单元格在哪里。

将不胜感激任何帮助。

谢谢

更新:2015年6月24日

苹果已经解决了iOS 9.0 SDK中的大部分错误。 所有的问题都是从iOS 9 beta 2开始修复的,包括滚动到表格视图的顶部和底部,而不是animation,在表格视图中间滚动时调用reloadData

以下是尚未解决的其余问题:

  1. 使用较大的估计行高时,使用animation滚动到最后一行会导致表格视图单元消失。
  2. 当使用较小的估计行高时,使用animation滚动到最后一行将导致表视图过早滚动,在可见区域下方留下一些单元格(最后一行仍然不在屏幕上)。

已经为与animation滚动有关的这些问题提交了一个新的错误报告(rdar:// 21539211)。

原始答复

这是Apple视图行高度估计的一个缺陷,自从iOS 7引入了这个function以来,它就一直存在。我直接和Apple UIKit的工程师和开发者就此问题进行了沟通 – 他们已经承认这是一个错误,但没有任何可靠的解决方法(缺less禁用行高度估计),并没有对解决它特别感兴趣。

请注意,该错误以其他方式显示,例如在部分或全部滚动(例如, contentOffset.y显着大于0)时调用reloadData时消失的表视图单元格。

显然,对于iOS 8自行确定尺寸的单元,行高估计非常重要,所以苹果真的需要尽快解决这个问题。

我在2013年10月21日作为雷达提交了这个问题#15283329。 请logging重复的错误报告,以便Apple优先考虑修复。

你可以附上这个简单的示例项目来演示这个问题。 它直接基于苹果自己的示例代码。

这是一个非常烦人的错误,但我认为我find了一个永久的解决scheme,但我不能完全解释为什么。

在一个小的(未被注意的)延迟之后调用函数:

 let delay = 0.1 * Double(NSEC_PER_SEC) let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) dispatch_after(time, dispatch_get_main_queue(), { tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true) }) 

请告诉我,如果这也适用于你。

这绝对是苹果的一个错误。 我也有这个问题。 我通过调用两次“scrollToRowAtIndexPath”方法解决了这个问题,例如:

  if array.count > 0 { let indexPath: NSIndexPath = NSIndexPath(forRow: array.count - 1, inSection: 0) self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true) let delay = 0.1 * Double(NSEC_PER_SEC) let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) dispatch_after(time, dispatch_get_main_queue(), { self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true) }) } 

我发现了一个暂时的解决方法,可能会有所帮助,直到苹果决定修复一直困扰着我们的错误。

 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *text = [self findTextForIndexPath:indexPath]; UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:13]; CGRect estimatedHeight = [text boundingRectWithSize:CGSizeMake(215, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: font} context:nil]; return TOP_PADDING + CGRectGetHeight(estimatedHeight) + BOTTOM_PADDING; } 

这并不完美,但它为我做了这个工作。 现在我可以打电话给:

 - (void)scrollToLastestSeenMessageAnimated:(BOOL)animated { NSInteger count = [self tableView:self.tableView numberOfRowsInSection:0]; if (count > 0) { NSInteger lastPos = MAX(0, count-1); [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:lastPos inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated]; } } 

viewDidLayoutSubviews ,它find底部的正确位置(或非常接近的估计位置)。

我希望有帮助。

对于我的情况,我发现一个暂时的解决方法,不build议估计的细胞高度的程序。 我通过在我的代码中注释掉以下方法来做到这一点:

 - (CGFloat) tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath 

但是,请注意,如果您的单元格相互之间变化很大,则可能会影响用户滚动时的用户体验。 就我而言,迄今为止没有明显的差异。

希望它有帮助!

我的解决scheme是使用故事板的大小作为估计。

所以,而不是这个:

 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewAutomaticDimension; 

}

我做了这样的事情:

 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { MyMessageType messageType = [self messageTypeForRowAtIndexPath:indexPath]; switch (messageType) { case MyMessageTypeText: return 45; break; case MyMessageTypeMaybeWithSomeMediaOrSomethingBiggerThanJustText: return 96; break; default: break; } } 

我正在写一个聊天表视图,所以很可能我的许多单元格,特别是文本types会比IB中的大,特别是如果聊天消息很长。 这似乎是一个相当不错的…嗯…估计和滚动到底部变得非常接近。 滚动时间越长,似乎越差,但这是我想的

只需在viewDidAppear之后调用tableview reloadData就可以解决问题

 -(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self.tableView reloadData]; } 

在故事板窗口中单击空白区域以取消select所有视图,然后单击具有表格视图的视图,然后单击Resolve Auto Layout Issue图标并selectReset to Suggested Constraints

在这里输入图像描述在这里输入图像描述

虽然smileyborg的答案是iOS 8.x中的错误,但它应该在您所支持的所有平台中进行修复。

为了解决iOS9之前的问题,下面的代码不需要任何dispatch_async或dispatch_after。 在iOS 8.4模拟器上testing。

更新 :调用(只)layoutIfNeeded不工作时,通过滚动UIPageViewController视图控制器变得可见。 所以使用layoutSubviews(或者setNeedsLayout + layoutIfNeeded)来代替。

 // For iOS 8 bug workaround. // See https://stackoverflow.com/a/33515872/1474113 - (void)scrollToBottomForPreiOS9 { CGFloat originalY, scrolledY; do { // Lay out visible cells immediately for current contentOffset. // NOTE: layoutIfNeeded does not work when hosting UIPageViewController is dragged. [self.tableView layoutSubviews]; originalY = self.tableView.contentOffset.y; [self scrollToBottom]; // Call -scrollToRowAtIndexPath as usual. scrolledY = self.tableView.contentOffset.y; } while (scrolledY > originalY); } 

创build具有不同高度的单元格的聊天tableView时,我遇到了同样的问题。 我在viewDidAppear()生命周期方法中调用下面的代码:

 // First figure out how many sections there are let lastSectionIndex = self.tableView.numberOfSections - 1 // Then grab the number of rows in the last section let lastRowIndex = self.tableView.numberOfRowsInSection(lastSectionIndex) - 1 // Now just construct the index path let pathToLastRow = NSIndexPath(forRow: lastRowIndex, inSection: lastSectionIndex) // Make the last row visible self.tableView.scrollToRowAtIndexPath(pathToLastRow, atScrollPosition: UITableViewScrollPosition.None, animated: true) 

请让我知道,如果这也适用于你。

使用这个简单的代码来滚动底部

  var rows:NSInteger=self.tableName.numberOfRowsInSection(0) if(rows > 0) { let indexPath = NSIndexPath(forRow: rows-1, inSection: 0) tableName.scrollToRowAtIndexPath(indexPath , atScrollPosition: UITableViewScrollPosition.Bottom, animated: true) } }