我正在构建一个具有用户提交帖子的Feed视图的应用.此视图具有UITableView
自定义UITableViewCell
实现.在这个单元格中,我有另一个UITableView
用于显示注释.要点是这样的:
Feed TableView
PostCell
Comments (TableView)
CommentCell
PostCell
Comments (TableView)
CommentCell
CommentCell
CommentCell
CommentCell
CommentCell
初始订阅源将下载3条评论进行预览,但如果有更多评论,或者用户添加或删除评论,我想PostCell
通过添加或删除CommentCells
评论表内部来更新Feed表视图内的位置的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
在预期内添加或删除了评论.我PostCells
在feed表中使用自动调整大小.PostCell
扩展的注释表显示所有注释,但动画有点不稳定,并且在进行单元格更新动画时,表格会向上和向下滚动十几个像素.
调整大小时的跳跃有点烦人,但后来我的主要问题出现了.现在,如果我在Feed中向下滚动,滚动就像以前一样平滑,但是如果我在单元格上方向上滚动,我只是在添加注释后调整大小,Feed会在到达Feed的顶部之前向后跳几次.我iOS8
为Feed 设置了自动调整大小的单元格,如下所示:
// (FeedController.swift)
// tableView is the feed table containing PostCells
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 560
如果我删除了estimatedRowHeight
,只要单元格高度发生变化,表格就会滚动到顶部.我现在感觉很困惑,作为一个新的iOS开发人员,可以使用你可能有的任何提示.
1> dosdos..:
这是我发现解决此类问题的最佳解决方案(滚动问题+ 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];
}
斯威夫特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)
}
对我来说,它仍然不起作用,我有一个自定义的imageView,并根据宽度和高度的尺寸来更新其高度约束.即使缓存了单元格高度,滚动仍然会跳跃,特别是在具有与屏幕上当前高度不同的新单元格滚动到屏幕之前.
@dosdos上帝保佑你,老兄!
对我来说这是一个更好的解决方 它完美地运作
真正简单有效的解决方案.谢谢!
2> Gabriel Cart..:
我们遇到了同样的问题.它来自对单元格高度的错误估计,导致SDK强制导致较高的高度,这会导致向上滚动时单元格的跳跃.根据您构建单元的方式,解决此问题的最佳方法是实现该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
}
@BryanAlger是对的.自动行高度不适用于具有大量高度差异的行的表.要获得可靠的平滑滚动,必须在heightForRowAtIndexPath方法中提供正确的结果,最好使用缓存的行高.否则,当tableView需要更新它的contentSize时,你会变得急躁,特别是在推送或显示另一个视图控制器并返回的情况下.不幸的是,自动行高仅适用于具有几行的简单tableView.
你尝试过我上面描述的方法吗?这是应该使用iOS 8的方式,不应该计算高度,因为框架已经处理它.如果实现heightForRowAtIndexPath方法,则只是覆盖SDK的行为.
在您实现heightForRowAtIndexPath的情况下,您没有使用自动单元格高度维度的功能.当然,实现它会起作用,但是你的细胞不是动态的.
3> 小智..:
dosdos的答案在Swift 2中为我工作
宣布伊娃
var heightAtIndexPath = NSMutableDictionary()
在func viewDidLoad()中
func viewDidLoad() {
.... your code
self.tableView.rowHeight = UITableViewAutomaticDimension
}
然后添加以下两种方法:
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 = [IndexPath: CGFloat]()
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return self.heightAtIndexPath[indexPath] ?? UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
self.heightAtIndexPath[indexPath] = cell.frame.size.height
}