可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Usually, in a table, the following code can be used to scroll to the bottom automatically:
let indexPath = NSIndexPath(forRow: rowCount - 1, inSection: 0) self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
However, when I set it to use automatic row heights in viewDidLoad() as shown below, the above code only scrolls roughly half way down the table (varies depending on row height and number of rows - it only happens with multiple rows much larger than estimatedRowHeight).
tableView.estimatedRowHeight = 50 tableView.rowHeight = UITableViewAutomaticDimension
How can I programatically scroll down to the bottom of a table whilst using UITableViewAutomaticDimension?
回答1:
I had a very similar problem when inserting rows to the bottom of a table view with automatic cell sizing, this is the only workaround I could find. It's not pretty but does the job (tested on iOS 8 and 9). At insertion time:
// compute the insertion index as the last one let insertionIndex = NSIndexPath(forRow: elements.count - 1, inSection: 0) // delete from the top, insert at the bottom tableView.beginUpdates() tableView.deleteRowsAtIndexPaths(deletionsIndexes, withRowAnimation: .Top) tableView.insertRowsAtIndexPaths([insertionIndex], withRowAnimation: .Fade) tableView.endUpdates() // scroll to the last row, position will be wrong tableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: .Bottom, animated: false) // scroll back to the one before, the actual scrolling will be done after inserting the cell if elements.count > 1 { let prevIndex = NSIndexPath(forRow: lastIndex.row - 1, inSection: 0) self.tableView.scrollToRowAtIndexPath(prevIndex, atScrollPosition: .Bottom, animated: false) }
as you can see we scroll to the last row just to trigger the row insertion then scroll back to the row before that (all without animation) to avoid any visible scrolling. Then when inserting the cell:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { [...] let cell = try configuredCell(forElement: element) dispatch_async(dispatch_get_main_queue()) { let lastIndex = NSIndexPath(forRow: self.elements.count - 1, inSection: 0) // when inserting the last element we do the actual animated scrolling if indexPath.row == self.elements.count - 1 { tableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: .Bottom, animated: true) } } return cell }
回答2:
I had the same issue and adding a delay like this helped me fixing this:
let delay = 0.1 * Double(NSEC_PER_SEC) let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) dispatch_after(time, dispatch_get_main_queue(), { let indexPath = NSIndexPath(forRow: rowCount - 1, inSection: 0) self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true) })
回答3:
In fact, the reason for this 'misbehavior' of UITableView is the row height estimation. When using dynamic cell height, the results of tableView.contentSize and for example [tableView rectForHeaderInSection: lastSection] are not in sync for items, that have not been shown yet (e.g. item with only estimated layout rects. The section rect for example may be outside the contentSize or somewhere inside, even with no cell inside).
In addition, it seems, that scollRectToVisible: will only work with valid rects. So calling it with currently wrong estimations of table view items may just lead to nothing...
My solution to this problem is a UITableView category which just iterates a few times to scroll to the desired position. So the UITableView has got a chance to calculate the real item layout rects (header, cell, footer). My tests have shown, that in most cases just one loop is needed.
UITableView+LEAScrollToVisible.h
UITableView+LEAScrollToVisible.m