UITableView 刷新不滚动
Posted
技术标签:
【中文标题】UITableView 刷新不滚动【英文标题】:UITableView Refresh without scrolling 【发布时间】:2014-08-31 06:36:02 【问题描述】:我有一个带有项目的_TableView
,我想设置自动刷新,我不希望它在刷新时滚动,假设用户向下滚动了 2 页,并且触发了刷新 -> 所以我想将刷新后的内容放在表格顶部,而不影响用户的滚动
假设用户在第 18 行
现在 _dataSource
已刷新,因此它获取了 4 个项目,所以我希望用户留在他原来的项目上。
实现它的最佳方法是什么??
【问题讨论】:
希望这些答案对您有所帮助:***.com/questions/5872759/… 您的表格视图单元格有静态高度还是动态高度?因为如果您的表格视图具有动态单元格高度,则无法在不滚动的情况下重新加载,表格视图必须在具有动态单元格时从头开始重新创建所有单元格。 【参考方案1】:对于 Swift 3+:
您需要保存UITableView
的当前偏移量,然后重新加载,然后将偏移量设置回UITableView
。
我为此目的创建了这个函数:
func reload(tableView: UITableView)
let contentOffset = tableView.contentOffset
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.setContentOffset(contentOffset, animated: false)
只需调用它:reload(tableView: self.tableView)
【讨论】:
如果tableview不改变偏移量仍然加载新单元格怎么办? @RockBalbao - 我不明白你的问题,兄弟 我正在使用 NSFetchedResultsControllerDelegate。现在我正在使用 UIRefreshControl 在 scrollDown 上加载旧单元格。在 performFetch 加载数据时,我只需要重新加载数据。但我不想显示重新加载的单元格。让它们在上面加载。当我在上面滚动时,只有它们变得可见。因此,为了实现这一点,我尝试使用这种 contentOffset 方式。但是在 reloadData 之前和 reloadData 之后 contentOffset 是相同的,尽管它在上面添加了几行 我觉得您的实现与此没有太大关系。因为在 reloadData 之前和之后让 contentOffset 相同正是关键所在。例如,如果您有一个“喜欢按钮”并且您正在使用实时数据库,这在这里很有用。防止tableView晃动。我建议在堆栈上打开一个线程并尽可能准确地描述您的问题。【参考方案2】:SWIFT 3
let contentOffset = self.tableView.contentOffset
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
self.tableView.setContentOffset(contentOffset, animated: false)
这是 ios8 在使用 UITableViewAutomatic Dimension 时的错误。我们需要存储表格的内容偏移量,重新加载表格,强制布局并设置 contenOffset。
CGPoint contentOffset = self.tableView.contentOffset;
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
[self.tableView setContentOffset:contentOffset];
【讨论】:
关于 iOS 8 的第一句话拯救了我的一天。 感谢分享代码。 拯救了我的一天。谢谢。 如果您有 50 行,每行首先高 30 点,然后是 3 行,每行高 3 点,我不确定这个答案是否有意义。想象一下,您滚动到底部并重新加载数据 - 之后您无法保持相同的滚动偏移量。【参考方案3】:我正在显示是否只添加了一行。您可以将其扩展到多行。
// dataArray is your data Source object
[dataArray insertObject:name atIndex:0];
CGPoint contentOffset = self.tableView.contentOffset;
contentOffset.y += [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView reloadData];
[self.tableView setContentOffset:contentOffset];
但要使其工作,您需要定义 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
方法。否则,如果它是恒定的,您可以直接给您的 tableview 行高。
【讨论】:
不是固定的,谢谢你的回答我会试试的, 是的,那么您肯定已经定义了- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
的方法。如果对您有帮助,请接受我的回答。【参考方案4】:
只需将estimatedRowHeight
设置为最大可能值。
self.tableView.estimatedRowHeight = 1000
self.tableView.estimatedSectionFooterHeight = 100.0
self.tableView.estimatedSectionHeaderHeight = 500.0
就是这样!!
注意:
请不要使用FLT_MAX, DBL_MAX
值。可能会导致您的应用崩溃。
【讨论】:
完美地为我工作 完美地为我工作 这对我也很有效,tx!我将这个答案与关于同一主题的另一个问题联系起来。【参考方案5】:我是这样做的:
messages.insertContentsOf(incomingMsgs.reverse(), at: 0)
table.reloadData()
// This is for the first load, first 20 messages, scroll to bottom
if (messages.count <= 20)
let indexToScroll = NSIndexPath(forRow: saferSelf.messages.count - 1, inSection: 0)
table.scrollToRowAtIndexPath(indexToScroll, atScrollPosition: .Top , animated: false)
// This is to reload older messages on top of tableview
else
let indexToScroll = NSIndexPath(forRow: incomingMsgs.count, inSection: 0)
table.scrollToRowAtIndexPath(indexToScroll, atScrollPosition: .Top , animated: false)
// Remove the refreshControl.height + tableHeader.height from the offset so the content remain where it was before reload
let theRightOffset = CGPointMake(0, table.contentOffset.y - refreshControl.frame.height - table.headeView.frame.height)
table.setContentOffset(theRightOffset, animated: false)
...另外,由于我使用动态单元格高度,为了避免一些奇怪,估计被缓存:
var heightAtIndexPath = [NSIndexPath: CGFloat]()
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
return heightAtIndexPath[indexPath] ?? UITableViewAutomaticDimension
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath)
heightAtIndexPath[indexPath] = cell.frame.height
【讨论】:
在我的情况下,单元缓存是唯一有效的方法,谢谢! :) 感谢关于缓存高度估计的好建议! @马可【参考方案6】:使用扩展
创建UITableViewExtensions.swift
并添加以下内容:
extension UITableView
func reloadDataWithoutScroll()
let offset = contentOffset
reloadData()
layoutIfNeeded()
setContentOffset(offset, animated: false)
【讨论】:
【参考方案7】:在iOS 12.x
中,使用Xcode 10.2.1
是一个更简单的选择。
UIView.performWithoutAnimation
let loc = tableView.contentOffset
tableView.reloadRows(at: [indexPath], with: .none)
tableView.contentOffset = loc
这比跟随更好;当行不完全可见时,它有时会晃动。
let contentOffset = self.tableView.contentOffset
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
self.tableView.setContentOffset(contentOffset, animated: false)
【讨论】:
【参考方案8】:此代码将防止不必要的动画并保持滚动视图的内容偏移,对我来说效果很好。
let lastScrollOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.reloadData()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastScrollOffset, animated: false)
【讨论】:
【参考方案9】:当你想重新加载时,你必须
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
也可以使用这个UITableViewDelegate
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
return 'your maximum cell's height'
并且您的 tableView 将保持在先前的滚动位置而不滚动
【讨论】:
【参考方案10】:Swift 4.2:简单的解决方案
override func viewDidLoad()
super.viewDidLoad()
self.tableView.estimatedRowHeight = 0
self.tableView.estimatedSectionHeaderHeight = 0
self.tableView.estimatedSectionFooterHeight = 0
//And then simply update(insert, reloadSections, delete etc) your tableView or reload
tableView.reloadData()
//or
UIView.performWithoutAnimation
tableView.beginUpdates()
.....
tableView.endUpdates()
【讨论】:
【参考方案11】:尝试替换
reloadData
与
tableView.reloadRows
(at: tableView!.indexPathsForVisibleRows!, with: .none
),
但是你应该关心no cells
,如果no cells
,这个方法应该会导致崩溃。
【讨论】:
【参考方案12】:我写了一些适合我的东西:
extension UIScrollView
func reloadDataAndKeepContentOffsetInPlace(reloadData:(() -> Void))
let currentContentHeight = contentSize.height
if currentContentHeight == .zero
reloadData()
return
reloadData()
layoutIfNeeded()
let newContentHeight = self.contentSize.height
DispatchQueue.main.async
var contentOffset = self.contentOffset
contentOffset.y += newContentHeight - currentContentHeight
self.setContentOffset(contentOffset, animated: false)
这样使用:
self.reloadSomeData()
collectionView.reloadDataAndKeepContentOffsetInPlace [weak self] in
guard let self = self else return
self.collectionView.reloadData()
【讨论】:
【参考方案13】:试试下面的。
tableView.estimatedRowHeight = 0
tableView.estimatedSectionHeaderHeight = 0
tableView.estimatedSectionFooterHeight = 0
来源:https://developer.apple.com/forums/thread/86703
【讨论】:
以上是关于UITableView 刷新不滚动的主要内容,如果未能解决你的问题,请参考以下文章
在主线程上调用 tableView.reloadData 后 UITableView 不刷新