在使用UITableViewAutomaticDimension更新UITableViewCell之后进行滚动
我正在构build一个具有用户提交的post的提要视图的应用程序。 这个视图有一个自定义UITableViewCell
实现的UITableViewCell
。 在这个单元UITableView
,我有另外一个显示注释的UITableView
。 要点是这样的:
Feed TableView PostCell Comments (TableView) CommentCell PostCell Comments (TableView) CommentCell CommentCell CommentCell CommentCell CommentCell
最初的Feed将下载3个评论用于预览,但是如果有更多评论,或者如果用户添加或删除评论,我想通过添加或删除CommentCells
到评论PostCell
来更新PostCell
中的PostCell
。 PostCell
里面的表。 我目前正在使用下面的帮手来完成这个工作:
// (PostCell.swift) Handle showing/hiding comments func animateAddOrDeleteComments(startRow: Int, endRow: Int, operation: CellOperation) { let table = self.superview?.superview as UITableView // "table" is outer feed table // self is the PostCell that is updating it's comments // self.comments is UITableView for displaying comments inside of the PostCell table.beginUpdates() self.comments.beginUpdates() // This function handles inserting/removing/reloading a range of comments // so we build out an array of index paths for each row that needs updating var indexPaths = [NSIndexPath]() for var index = startRow; index <= endRow; index++ { indexPaths.append(NSIndexPath(forRow: index, inSection: 0)) } switch operation { case .INSERT: self.comments.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None) case .DELETE: self.comments.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None) case .RELOAD: self.comments.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None) } self.comments.endUpdates() table.endUpdates() // trigger a call to updateConstraints so that we can update the height constraint // of the comments table to fit all of the comments self.setNeedsUpdateConstraints() } override func updateConstraints() { super.updateConstraints() self.commentsHeight.constant = self.comments.sizeThatFits(UILayoutFittingCompressedSize).height }
这完成更新就好了。 如预期的那样,该PostCell
在PostCell
内部添加或删除的注释被更新。 我正在Feed表中使用自动调整PostCells
的大小。 PostCell
的注释表扩展为显示所有的注释,但animation有点生涩,并且在单元更新animation发生时,上下滚动十几个像素。
在resize的过程中跳跃有点烦人,但是我的主要问题随之而来。 现在,如果我在Feed中向下滚动,滚动就像之前一样平滑,但是如果我在添加注释后resize的单元格上方滚动,则Feed会在向Feed顶部前几次向后跳跃。 我设置iOS8
自动resize的单元格像这样:
// (FeedController.swift) // tableView is the feed table containing PostCells self.tableView.rowHeight = UITableViewAutomaticDimension self.tableView.estimatedRowHeight = 560
如果我删除estimatedRowHeight
,则只要单元格高度发生变化,表格就会滚动到顶部。 现在,我感觉现在已经很困难了,作为一个新的iOS开发者,可以使用你可能有的任何提示。
这里是我发现解决这种问题的最佳解决scheme(滚动问题+ reloadRows + iOS 8 UITableViewAutomaticDimension);
它包括保持每一个字典高度和更新(在字典中),因为tableView将显示单元格。
然后你将返回保存的高度- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
方法。
你应该实现这样的东西:
Objective-C的
- (void)viewDidLoad { [super viewDidLoad]; self.heightAtIndexPath = [NSMutableDictionary new]; self.tableView.rowHeight = UITableViewAutomaticDimension; } - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { NSNumber *height = [self.heightAtIndexPath objectForKey:indexPath]; if(height) { return height.floatValue; } else { return UITableViewAutomaticDimension; } } - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { NSNumber *height = @(cell.frame.size.height); [self.heightAtIndexPath setObject:height forKey:indexPath]; }
Swift 3
@IBOutlet var tableView : UITableView? var heightAtIndexPath = NSMutableDictionary() override func viewDidLoad() { super.viewDidLoad() tableView?.rowHeight = UITableViewAutomaticDimension } func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { if let height = heightAtIndexPath.object(forKey: indexPath) as? NSNumber { return CGFloat(height.floatValue) } else { return UITableViewAutomaticDimension } } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { let height = NSNumber(value: Float(cell.frame.size.height)) heightAtIndexPath.setObject(height, forKey: indexPath as NSCopying) }
我们有同样的问题。 它来自于对单元格高度的错误估计,导致SDK强制高度不好,这将导致滚动时单元格的跳跃。 根据你如何构build你的单元格,解决这个问题的最好方法是实现UITableViewDelegate
方法- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
只要你的估计非常接近细胞高度的实际值,这几乎可以消除跳跃和急动。 下面是我们如何实现它,你会得到的逻辑:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { // This method will get your cell identifier based on your data NSString *cellType = [self reuseIdentifierForIndexPath:indexPath]; if ([cellType isEqualToString:kFirstCellIdentifier]) return kFirstCellHeight; else if ([cellType isEqualToString:kSecondCellIdentifier]) return kSecondCellHeight; else if ([cellType isEqualToString:kThirdCellIdentifier]) return kThirdCellHeight; else { return UITableViewAutomaticDimension; } }
增加了Swift 2支持
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { // This method will get your cell identifier based on your data let cellType = reuseIdentifierForIndexPath(indexPath) if cellType == kFirstCellIdentifier return kFirstCellHeight else if cellType == kSecondCellIdentifier return kSecondCellHeight else if cellType == kThirdCellIdentifier return kThirdCellHeight else return UITableViewAutomaticDimension }
dosdos答案在Swift 2中为我工作
宣布伊娃
var heightAtIndexPath = NSMutableDictionary()
在func viewDidLoad()
func viewDidLoad() { .... your code self.tableView.rowHeight = UITableViewAutomaticDimension }
然后添加以下2个方法:
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { let height = self.heightAtIndexPath.objectForKey(indexPath) if ((height) != nil) { return CGFloat(height!.floatValue) } else { return UITableViewAutomaticDimension } } override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let height = cell.frame.size.height self.heightAtIndexPath.setObject(height, forKey: indexPath) }
SWIFT 3:
var heightAtIndexPath = NSMutableDictionary() func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { let height = self.heightAtIndexPath.object(forKey: indexPath) if ((height) != nil) { return CGFloat(height as! CGFloat) } else { return UITableViewAutomaticDimension } } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { let height = cell.frame.size.height self.heightAtIndexPath.setObject(height, forKey: indexPath as NSCopying) }
我也面临同样的问题。 我find了一个解决方法,但并没有完全解决这个问题。 但是与之前的波涛汹涌的滚动相比,它似乎要好很多。
在你的UITableView
委托方法中:cellForRowAtIndexPath:
在返回单元格之前尝试使用以下两种方法来更新约束。 (Swift语言)
cell.setNeedsUpdateConstraints() cell.updateConstraintsIfNeeded()
编辑:你可能也必须玩tableView.estimatedRowHeight
值来获得更平滑的滚动。
在@dosdos之后回答。
我也发现有趣的实现: tableView(tableView: didEndDisplayingCell: forRowAtIndexPath:
特别是对于我的代码,其中单元格正在dynamic地更改约束,而单元格已经显示在屏幕上。 像这样更新字典有助于第二次显示单元格。
var heightAtIndexPath = [NSIndexPath : NSNumber]() .... tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = UITableViewAutomaticDimension .... extension TableViewViewController: UITableViewDelegate { //MARK: - UITableViewDelegate func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { let height = heightAtIndexPath[indexPath] if let height = height { return CGFloat(height) } else { return UITableViewAutomaticDimension } } func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let height: NSNumber = CGRectGetHeight(cell.frame) heightAtIndexPath[indexPath] = height } func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let height: NSNumber = CGRectGetHeight(cell.frame) heightAtIndexPath[indexPath] = height } }
@dosdos解决scheme工作正常
但是你应该添加一些东西
以下@dosdos的答案
Swift 3/4
@IBOutlet var tableView : UITableView! var heightAtIndexPath = NSMutableDictionary() override func viewDidLoad() { super.viewDidLoad() tableView?.rowHeight = UITableViewAutomaticDimension } func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { if let height = heightAtIndexPath.object(forKey: indexPath) as? NSNumber { return CGFloat(height.floatValue) } else { return UITableViewAutomaticDimension } } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { let height = NSNumber(value: Float(cell.frame.size.height)) heightAtIndexPath.setObject(height, forKey: indexPath as NSCopying) }
然后使用这条线,当你想要的,对我来说,我用它在textDidChange内
- 首先重新加载Tableview
- 更新约束
-
最后移到桌面顶端
tableView.reloadData() self.tableView.layoutIfNeeded() self.tableView.setContentOffset(CGPoint.zero, animated: true)