具有动态单元格高度的 UITableView 的 reloadData() 导致跳动滚动
reloadData() of UITableView with Dynamic cell heights causes jumpy scrolling
我觉得这可能是一个常见问题,想知道是否有任何通用的解决方案。
基本上,我的 UITableView 的每个单元格都有动态单元格高度。如果我不在 UITableView 的顶部并且 tableView.reloadData()
,向上滚动会变得不稳定。
我认为这是因为我重新加载了数据,当我向上滚动时,UITableView 正在重新计算每个可见单元格的高度。我该如何缓解这种情况,或者我如何只将某个 IndexPath 中的数据重新加载到 UITableView 的末尾?
此外,当我确实设法一直滚动到顶部时,我可以向下滚动然后向上滚动,没有跳跃的问题。这很可能是因为已经计算了 UITableViewCell 的高度。
实际上,您可以使用 reloadRowsAtIndexPaths
仅重新加载某些行,例如:
tableView.reloadRowsAtIndexPaths(indexPathArray, withRowAnimation: UITableViewRowAnimation.None)
但是,一般来说,您还可以为 table 单元格高度变化设置动画,如下所示:
tableView.beginUpdates()
tableView.endUpdates()
在返回 func cellForRowAtIndexPath(_ indexPath: NSIndexPath) -> UITableViewCell?
中的单元格之前尝试调用 cell.layoutSubviews()
。这是 iOS8.
中的已知错误
我今天运行进入这个并观察到:
- 确实只有 iOS 8 个。
- 覆盖
cellForRowAtIndexPath
没有帮助。
修复实际上非常简单:
覆盖 estimatedHeightForRowAtIndexPath
并确保它 returns 正确的值。
有了这个,我的 UITableViews 中所有奇怪的抖动和跳跃都停止了。
注意:我实际上知道我的单元格的大小。只有两个可能的值。如果您的单元格确实是可变大小的,那么您可能希望缓存 tableView:willDisplayCell:forRowAtIndexPath:
中的 cell.bounds.size.height
跳跃是因为估计高度错误。 estimatedRowHeight 与实际高度的差异越大,table 在重新加载时跳动的可能性就越大,尤其是向下滚动得越远。这是因为 table 的估计大小与其实际大小根本不同,迫使 table 调整其内容大小和偏移量。
所以估计的高度不应该是一个随机值,而是接近你认为的高度。我设置UITableViewAutomaticDimension
的时候也遇到过
如果您的细胞类型相同,则
func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 100//close to your cell height
}
如果你在不同的部分有各种各样的单元格,那么我认为更好的地方是
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
//return different sizes for different cells if you need to
return 100
}
为了防止跳跃,你应该在加载时保存单元格的高度,并在 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:
// 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;
}
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
}
@Igor 答案在这种情况下工作正常,Swift-4
它的代码。
// declaration & initialization
var cellHeightsDictionary: [IndexPath: CGFloat] = [:]
按照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
}
我已经尝试了上面的所有解决方法,但没有任何效果。
在花费数小时并经历了所有可能的挫折之后,找到了解决此问题的方法。这个解决方案是一个救星!工作得很好!
Swift 4
let lastContentOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastContentOffset, animated: false)
我将其添加为扩展,以使代码看起来更清晰,并避免每次我想重新加载时都编写所有这些行。
extension UITableView {
func reloadWithoutAnimation() {
let lastScrollOffset = contentOffset
beginUpdates()
endUpdates()
layer.removeAllAnimations()
setContentOffset(lastScrollOffset, animated: false)
}
}
终于..
tableView.reloadWithoutAnimation()
OR 你实际上可以在你的 UITableViewCell
awakeFromNib()
方法中添加这些行
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
并正常进行reloadData()
这里有一个更短的版本:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return self.cellHeightsDictionary[indexPath] ?? UITableViewAutomaticDimension
}
有一个 bug,我相信它是在 iOS11 中引入的。
那就是当您执行 reload
时,tableView contentOffSet
会意外更改。事实上 contentOffset
不应该在重新加载后改变。它往往是由于 UITableViewAutomaticDimension
的错误计算而发生的
您必须保存 contentOffSet
并在重新加载完成后将其设置回您保存的值。
func reloadTableOnMain(with offset: CGPoint = CGPoint.zero){
DispatchQueue.main.async { [weak self] () in
self?.tableView.reloadData()
self?.tableView.layoutIfNeeded()
self?.tableView.contentOffset = offset
}
}
你是怎么使用它的?
someFunctionThatMakesChangesToYourDatasource()
let offset = tableview.contentOffset
reloadTableOnMain(with: offset)
这个答案来自here
这个在 Swift4 中对我有用:
extension UITableView {
func reloadWithoutAnimation() {
let lastScrollOffset = contentOffset
reloadData()
layoutIfNeeded()
setContentOffset(lastScrollOffset, animated: false)
}
}
None 这些解决方案对我有用。这是我对 Swift 4 & Xcode 10.1...
所做的
在 viewDidLoad() 中,声明 table 动态行高并在单元格中创建正确的约束...
tableView.rowHeight = UITableView.automaticDimension
同样在 viewDidLoad() 中,将所有 tableView 单元格笔尖注册到 tableview,如下所示:
tableView.register(UINib(nibName: "YourTableViewCell", bundle: nil), forCellReuseIdentifier: "YourTableViewCell")
tableView.register(UINib(nibName: "YourSecondTableViewCell", bundle: nil), forCellReuseIdentifier: "YourSecondTableViewCell")
tableView.register(UINib(nibName: "YourThirdTableViewCell", bundle: nil), forCellReuseIdentifier: "YourThirdTableViewCell")
在 tableView heightForRowAt 中,return 高度等于 indexPath.row 处每个单元格的高度...
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
let cell = Bundle.main.loadNibNamed("YourTableViewCell", owner: self, options: nil)?.first as! YourTableViewCell
return cell.layer.frame.height
} else if indexPath.row == 1 {
let cell = Bundle.main.loadNibNamed("YourSecondTableViewCell", owner: self, options: nil)?.first as! YourSecondTableViewCell
return cell.layer.frame.height
} else {
let cell = Bundle.main.loadNibNamed("YourThirdTableViewCell", owner: self, options: nil)?.first as! YourThirdTableViewCell
return cell.layer.frame.height
}
}
现在在 tableView estimatedHeightForRowAt 中给出每个单元格的估计行高。尽可能准确...
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
return 400 // or whatever YourTableViewCell's height is
} else if indexPath.row == 1 {
return 231 // or whatever YourSecondTableViewCell's height is
} else {
return 216 // or whatever YourThirdTableViewCell's height is
}
}
那应该行得通...
我在调用 tableView.reloadData()
时不需要保存和设置 contentOffset
我有 2 种不同的单元格高度。
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cellHeight = CGFloat(checkIsCleanResultSection(index: indexPath.row) ? 130 : 160)
return Helper.makeDeviceSpecificCommonSize(cellHeight)
}
我加上estimatedHeightForRowAt后,就没有再跳了
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let cellHeight = CGFloat(checkIsCleanResultSection(index: indexPath.row) ? 130 : 160)
return Helper.makeDeviceSpecificCommonSize(cellHeight)
}
用高值覆盖 estimatedHeightForRowAtIndexPath 方法,例如 300f
这应该可以解决问题:)
我使用了更多的方法来修复它:
对于视图控制器:
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
}
作为 UITableView 的扩展
extension UITableView {
func reloadSectionWithoutAnimation(section: Int) {
UIView.performWithoutAnimation {
let offset = self.contentOffset
self.reloadSections(IndexSet(integer: section), with: .none)
self.contentOffset = offset
}
}
}
结果是
tableView.reloadSectionWithoutAnimation(section: indexPath.section)
您可以在ViewDidLoad()
中使用以下内容
tableView.estimatedRowHeight = 0 // if have just tableViewCells <br/>
// use this if you have tableview Header/footer <br/>
tableView.estimatedSectionFooterHeight = 0 <br/>
tableView.estimatedSectionHeaderHeight = 0
我有这种跳跃行为,我最初能够通过设置准确的估计 header 高度来减轻它(因为我只有 1 个可能的 header 视图),但是跳跃随后开始具体发生在 header 内,不再影响整个 table。
按照这里的答案,我得到了它与动画有关的线索,所以我发现 table 视图在堆栈视图中,有时我们会在其中调用 stackView.layoutIfNeeded()
一个动画块。我的最终解决方案是确保除非 "really" 需要,否则不会发生此调用,因为布局 "if needed" 在该上下文中具有视觉行为,即使 "not needed".
我遇到了同样的问题。我有没有动画的分页和重新加载数据,但它没有帮助滚动防止跳跃。我有不同尺寸的 iPhone,滚动条在 iphone8 上不跳动,但在 iphone7+
上跳动
我对 viewDidLoad 函数应用了以下更改:
self.myTableView.estimatedRowHeight = 0.0
self.myTableView.estimatedSectionFooterHeight = 0
self.myTableView.estimatedSectionHeaderHeight = 0
我的问题解决了。希望对你也有帮助。
我发现解决这个问题的方法之一是
CATransaction.begin()
UIView.setAnimationsEnabled(false)
CATransaction.setCompletionBlock {
UIView.setAnimationsEnabled(true)
}
tableView.reloadSections([indexPath.section], with: .none)
CATransaction.commit()
其实我发现如果你使用reloadRows
会导致跳转问题。那么你应该尝试像这样使用 reloadSections
:
UIView.performWithoutAnimation {
tableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: .none)
}
对我来说,它适用于“heightForRowAt”
extension APICallURLSessionViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
print("Inside heightForRowAt")
return 130.50
}
}
对我来说,可行的解决方案是
UIView.setAnimationsEnabled(false)
tableView.performBatchUpdates { [weak self] in
self?.tableView.reloadRows(at: [indexPath], with: .none)
} completion: { [weak self] _ in
UIView.setAnimationsEnabled(true)
self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true) // remove if you don't need to scroll
}
我有可扩展的单元格。
我觉得这可能是一个常见问题,想知道是否有任何通用的解决方案。
基本上,我的 UITableView 的每个单元格都有动态单元格高度。如果我不在 UITableView 的顶部并且 tableView.reloadData()
,向上滚动会变得不稳定。
我认为这是因为我重新加载了数据,当我向上滚动时,UITableView 正在重新计算每个可见单元格的高度。我该如何缓解这种情况,或者我如何只将某个 IndexPath 中的数据重新加载到 UITableView 的末尾?
此外,当我确实设法一直滚动到顶部时,我可以向下滚动然后向上滚动,没有跳跃的问题。这很可能是因为已经计算了 UITableViewCell 的高度。
实际上,您可以使用 reloadRowsAtIndexPaths
仅重新加载某些行,例如:
tableView.reloadRowsAtIndexPaths(indexPathArray, withRowAnimation: UITableViewRowAnimation.None)
但是,一般来说,您还可以为 table 单元格高度变化设置动画,如下所示:
tableView.beginUpdates()
tableView.endUpdates()
在返回 func cellForRowAtIndexPath(_ indexPath: NSIndexPath) -> UITableViewCell?
中的单元格之前尝试调用 cell.layoutSubviews()
。这是 iOS8.
我今天运行进入这个并观察到:
- 确实只有 iOS 8 个。
- 覆盖
cellForRowAtIndexPath
没有帮助。
修复实际上非常简单:
覆盖 estimatedHeightForRowAtIndexPath
并确保它 returns 正确的值。
有了这个,我的 UITableViews 中所有奇怪的抖动和跳跃都停止了。
注意:我实际上知道我的单元格的大小。只有两个可能的值。如果您的单元格确实是可变大小的,那么您可能希望缓存 tableView:willDisplayCell:forRowAtIndexPath:
cell.bounds.size.height
跳跃是因为估计高度错误。 estimatedRowHeight 与实际高度的差异越大,table 在重新加载时跳动的可能性就越大,尤其是向下滚动得越远。这是因为 table 的估计大小与其实际大小根本不同,迫使 table 调整其内容大小和偏移量。
所以估计的高度不应该是一个随机值,而是接近你认为的高度。我设置UITableViewAutomaticDimension
的时候也遇到过
如果您的细胞类型相同,则
func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 100//close to your cell height
}
如果你在不同的部分有各种各样的单元格,那么我认为更好的地方是
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
//return different sizes for different cells if you need to
return 100
}
为了防止跳跃,你应该在加载时保存单元格的高度,并在 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:
// 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;
}
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
}
@Igor 答案在这种情况下工作正常,Swift-4
它的代码。
// declaration & initialization
var cellHeightsDictionary: [IndexPath: CGFloat] = [:]
按照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
}
我已经尝试了上面的所有解决方法,但没有任何效果。
在花费数小时并经历了所有可能的挫折之后,找到了解决此问题的方法。这个解决方案是一个救星!工作得很好!
Swift 4
let lastContentOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastContentOffset, animated: false)
我将其添加为扩展,以使代码看起来更清晰,并避免每次我想重新加载时都编写所有这些行。
extension UITableView {
func reloadWithoutAnimation() {
let lastScrollOffset = contentOffset
beginUpdates()
endUpdates()
layer.removeAllAnimations()
setContentOffset(lastScrollOffset, animated: false)
}
}
终于..
tableView.reloadWithoutAnimation()
OR 你实际上可以在你的 UITableViewCell
awakeFromNib()
方法中添加这些行
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
并正常进行reloadData()
这里有一个更短的版本:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return self.cellHeightsDictionary[indexPath] ?? UITableViewAutomaticDimension
}
有一个 bug,我相信它是在 iOS11 中引入的。
那就是当您执行 reload
时,tableView contentOffSet
会意外更改。事实上 contentOffset
不应该在重新加载后改变。它往往是由于 UITableViewAutomaticDimension
您必须保存 contentOffSet
并在重新加载完成后将其设置回您保存的值。
func reloadTableOnMain(with offset: CGPoint = CGPoint.zero){
DispatchQueue.main.async { [weak self] () in
self?.tableView.reloadData()
self?.tableView.layoutIfNeeded()
self?.tableView.contentOffset = offset
}
}
你是怎么使用它的?
someFunctionThatMakesChangesToYourDatasource()
let offset = tableview.contentOffset
reloadTableOnMain(with: offset)
这个答案来自here
这个在 Swift4 中对我有用:
extension UITableView {
func reloadWithoutAnimation() {
let lastScrollOffset = contentOffset
reloadData()
layoutIfNeeded()
setContentOffset(lastScrollOffset, animated: false)
}
}
None 这些解决方案对我有用。这是我对 Swift 4 & Xcode 10.1...
所做的在 viewDidLoad() 中,声明 table 动态行高并在单元格中创建正确的约束...
tableView.rowHeight = UITableView.automaticDimension
同样在 viewDidLoad() 中,将所有 tableView 单元格笔尖注册到 tableview,如下所示:
tableView.register(UINib(nibName: "YourTableViewCell", bundle: nil), forCellReuseIdentifier: "YourTableViewCell")
tableView.register(UINib(nibName: "YourSecondTableViewCell", bundle: nil), forCellReuseIdentifier: "YourSecondTableViewCell")
tableView.register(UINib(nibName: "YourThirdTableViewCell", bundle: nil), forCellReuseIdentifier: "YourThirdTableViewCell")
在 tableView heightForRowAt 中,return 高度等于 indexPath.row 处每个单元格的高度...
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
let cell = Bundle.main.loadNibNamed("YourTableViewCell", owner: self, options: nil)?.first as! YourTableViewCell
return cell.layer.frame.height
} else if indexPath.row == 1 {
let cell = Bundle.main.loadNibNamed("YourSecondTableViewCell", owner: self, options: nil)?.first as! YourSecondTableViewCell
return cell.layer.frame.height
} else {
let cell = Bundle.main.loadNibNamed("YourThirdTableViewCell", owner: self, options: nil)?.first as! YourThirdTableViewCell
return cell.layer.frame.height
}
}
现在在 tableView estimatedHeightForRowAt 中给出每个单元格的估计行高。尽可能准确...
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
return 400 // or whatever YourTableViewCell's height is
} else if indexPath.row == 1 {
return 231 // or whatever YourSecondTableViewCell's height is
} else {
return 216 // or whatever YourThirdTableViewCell's height is
}
}
那应该行得通...
我在调用 tableView.reloadData()
时不需要保存和设置 contentOffset我有 2 种不同的单元格高度。
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cellHeight = CGFloat(checkIsCleanResultSection(index: indexPath.row) ? 130 : 160)
return Helper.makeDeviceSpecificCommonSize(cellHeight)
}
我加上estimatedHeightForRowAt后,就没有再跳了
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let cellHeight = CGFloat(checkIsCleanResultSection(index: indexPath.row) ? 130 : 160)
return Helper.makeDeviceSpecificCommonSize(cellHeight)
}
用高值覆盖 estimatedHeightForRowAtIndexPath 方法,例如 300f
这应该可以解决问题:)
我使用了更多的方法来修复它:
对于视图控制器:
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
}
作为 UITableView 的扩展
extension UITableView {
func reloadSectionWithoutAnimation(section: Int) {
UIView.performWithoutAnimation {
let offset = self.contentOffset
self.reloadSections(IndexSet(integer: section), with: .none)
self.contentOffset = offset
}
}
}
结果是
tableView.reloadSectionWithoutAnimation(section: indexPath.section)
您可以在ViewDidLoad()
tableView.estimatedRowHeight = 0 // if have just tableViewCells <br/>
// use this if you have tableview Header/footer <br/>
tableView.estimatedSectionFooterHeight = 0 <br/>
tableView.estimatedSectionHeaderHeight = 0
我有这种跳跃行为,我最初能够通过设置准确的估计 header 高度来减轻它(因为我只有 1 个可能的 header 视图),但是跳跃随后开始具体发生在 header 内,不再影响整个 table。
按照这里的答案,我得到了它与动画有关的线索,所以我发现 table 视图在堆栈视图中,有时我们会在其中调用 stackView.layoutIfNeeded()
一个动画块。我的最终解决方案是确保除非 "really" 需要,否则不会发生此调用,因为布局 "if needed" 在该上下文中具有视觉行为,即使 "not needed".
我遇到了同样的问题。我有没有动画的分页和重新加载数据,但它没有帮助滚动防止跳跃。我有不同尺寸的 iPhone,滚动条在 iphone8 上不跳动,但在 iphone7+
上跳动我对 viewDidLoad 函数应用了以下更改:
self.myTableView.estimatedRowHeight = 0.0
self.myTableView.estimatedSectionFooterHeight = 0
self.myTableView.estimatedSectionHeaderHeight = 0
我的问题解决了。希望对你也有帮助。
我发现解决这个问题的方法之一是
CATransaction.begin()
UIView.setAnimationsEnabled(false)
CATransaction.setCompletionBlock {
UIView.setAnimationsEnabled(true)
}
tableView.reloadSections([indexPath.section], with: .none)
CATransaction.commit()
其实我发现如果你使用reloadRows
会导致跳转问题。那么你应该尝试像这样使用 reloadSections
:
UIView.performWithoutAnimation {
tableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: .none)
}
对我来说,它适用于“heightForRowAt”
extension APICallURLSessionViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
print("Inside heightForRowAt")
return 130.50
}
}
对我来说,可行的解决方案是
UIView.setAnimationsEnabled(false)
tableView.performBatchUpdates { [weak self] in
self?.tableView.reloadRows(at: [indexPath], with: .none)
} completion: { [weak self] _ in
UIView.setAnimationsEnabled(true)
self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true) // remove if you don't need to scroll
}
我有可扩展的单元格。