ios 具有动态单元格高度的 UITableView 的 reloadData() 导致跳跃式滚动
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28244475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
reloadData() of UITableView with Dynamic cell heights causes jumpy scrolling
提问by David
I feel like this might be a common issue and was wondering if there was any common solution to it.
我觉得这可能是一个常见问题,想知道是否有任何通用的解决方案。
Basically, my UITableView has dynamic cell heights for every cell. If I am not at the top of the UITableView and I tableView.reloadData()
, scrolling up becomes jumpy.
基本上,我的 UITableView 具有每个单元格的动态单元格高度。如果我不在 UITableView 和 I 的顶部tableView.reloadData()
,向上滚动就会变得跳跃。
I believe this is due to the fact that because I reloaded data, as I'm scrolling up, the UITableView is recalculating the height for each cell coming into visibility. How do I mitigate that, or how do I only reloadData from a certain IndexPath to the end of the UITableView?
我相信这是因为我重新加载了数据,当我向上滚动时,UITableView 正在重新计算每个单元格的高度。我该如何缓解这种情况,或者如何仅将数据从某个 IndexPath 重新加载到 UITableView 的末尾?
Further, when I do manage to scroll all the way to the top, I can scroll back down and then up, no problem with no jumping. This is most likely because the UITableViewCell heights were already calculated.
此外,当我设法一直滚动到顶部时,我可以向下滚动然后向上滚动,没有跳跃的问题。这很可能是因为 UITableViewCell 的高度已经计算出来了。
回答by Igor
To prevent jumping you should save heights of cells when they loads and give exact value in tableView:estimatedHeightForRowAtIndexPath
:
为了防止跳跃,您应该在加载时保存单元格的高度并在 中给出准确的值tableView:estimatedHeightForRowAtIndexPath
:
Swift:
迅速:
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] ?? UITableView.automaticDimension
}
Objective C:
目标 C:
// declare cellHeightsDictionary
NSMutableDictionary *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;
}
回答by Casey Wagner
Swift 3 version of accepted answer.
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
}
回答by Krishna Kishore
The jump is because of a bad estimated height. The more the estimatedRowHeight differs from the actual height the more the table may jump when it is reloaded especially the further down it has been scrolled. This is because the table's estimated size radically differs from its actual size, forcing the table to adjust its content size and offset.
So the estimated height shouldn't be a random value but close to what you think the height is going to be. I have also experienced when i set UITableViewAutomaticDimension
if your cells are same type then
跳跃是因为估计的高度错误。估计的行高度与实际高度的差异越大,表格在重新加载时可能会跳得越多,尤其是滚动得越远。这是因为表的估计大小与其实际大小完全不同,迫使表调整其内容大小和偏移量。所以估计的高度不应该是一个随机值,而是接近你认为的高度。当我设置UITableViewAutomaticDimension
您的单元格是否相同类型时,我也经历过
func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 100//close to your cell height
}
if you have variety of cells in different sections then I think the better place is
如果您在不同部分有各种单元格,那么我认为更好的地方是
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
//return different sizes for different cells if you need to
return 100
}
回答by Kiran Jasvanee
@Igoranswer is working fine in this case, Swift-4
code of it.
@Igor 的答案在这种情况下工作正常Swift-4
,它的代码。
// declaration & initialization
var cellHeightsDictionary: [IndexPath: CGFloat] = [:]
in following methods of UITableViewDelegate
在以下方法中 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 UITableView.automaticDimension
}
回答by Srujan Simha
I have tried all the workarounds above, but nothing worked.
我已经尝试了上述所有解决方法,但没有任何效果。
After spending hours and going through all the possible frustrations, figured out a way to fix this. This solution is a life savior! Worked like a charm!
在花了几个小时并经历了所有可能的挫折之后,找到了解决这个问题的方法。这个解决方案简直就是救命稻草!像魅力一样工作!
Swift 4
斯威夫特 4
let lastContentOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastContentOffset, animated: false)
I added it as an extension, to make the code look cleaner and avoid writing all these lines every time I want to reload.
我添加了它作为扩展,使代码看起来更干净,避免每次我想重新加载时都写这些行。
extension UITableView {
func reloadWithoutAnimation() {
let lastScrollOffset = contentOffset
beginUpdates()
endUpdates()
layer.removeAllAnimations()
setContentOffset(lastScrollOffset, animated: false)
}
}
finally ..
最后 ..
tableView.reloadWithoutAnimation()
ORyou could actually add these line in your UITableViewCell
awakeFromNib()
method
或者你实际上可以在你的UITableViewCell
awakeFromNib()
方法中添加这些行
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
and do normal reloadData()
并做正常 reloadData()
回答by rastislv
I use more ways how to fix it:
我使用更多方法来解决它:
For view controller:
对于视图控制器:
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
}
as the extension for UITableView
作为 UITableView 的扩展
extension UITableView {
func reloadSectionWithouAnimation(section: Int) {
UIView.performWithoutAnimation {
let offset = self.contentOffset
self.reloadSections(IndexSet(integer: section), with: .none)
self.contentOffset = offset
}
}
}
The result is
结果是
tableView.reloadSectionWithouAnimation(section: indexPath.section)
回答by MarcWan
I ran into this today and observed:
我今天遇到了这个并观察到:
- It's iOS 8 only, indeed.
- Overridding
cellForRowAtIndexPath
doesn't help.
- 确实,它仅适用于 iOS 8。
- 覆盖
cellForRowAtIndexPath
没有帮助。
The fix was actually pretty simple:
修复实际上非常简单:
Override estimatedHeightForRowAtIndexPath
and make sure it returns the correct values.
覆盖estimatedHeightForRowAtIndexPath
并确保它返回正确的值。
With this, all weird jittering and jumping around in my UITableViews has stopped.
有了这个,我的 UITableViews 中所有奇怪的抖动和跳跃都停止了。
NOTE: I actually know the size of my cells. There are only two possible values. If your cells are truly variable-sized, then you might want to cache the cell.bounds.size.height
from tableView:willDisplayCell:forRowAtIndexPath:
注意:我实际上知道我的单元格的大小。只有两个可能的值。如果您的单元格确实是可变大小的,那么您可能希望缓存cell.bounds.size.height
来自tableView:willDisplayCell:forRowAtIndexPath:
回答by Lyndsey Scott
You can in fact reload only certain rows by using reloadRowsAtIndexPaths
, ex:
实际上,您可以通过使用仅重新加载某些行reloadRowsAtIndexPaths
,例如:
tableView.reloadRowsAtIndexPaths(indexPathArray, withRowAnimation: UITableViewRowAnimation.None)
But, in general, you could also animate table cell height changes like so:
但是,一般来说,您还可以为表格单元格高度的变化设置动画,如下所示:
tableView.beginUpdates()
tableView.endUpdates()
回答by jake1981
Here's a bit shorter version:
这是一个更短的版本:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return self.cellHeightsDictionary[indexPath] ?? UITableViewAutomaticDimension
}
回答by Flappy
Overriding the estimatedHeightForRowAtIndexPath method with an high value, for example 300f
用高值覆盖estimatedHeightForRowAtIndexPath 方法,例如300f
This should fix the problem :)
这应该可以解决问题:)