ios 调用-reloadData后如何保持UITableView contentoffset
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8640409/
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
How to keep UITableView contentoffset after calling -reloadData
提问by avincross
CGPoint offset = [_table contentOffset];
[_table reloadData];
[_table setContentOffset:offset animated:NO]; //unuseful
// __block UITableView *tableBlock = _table;
// [self performBlock:^(id sender) {
// [tableBlock setContentOffset:offset];
// } afterDelay:2];
I know don't know of any delegate method which gets called after reloadData
.
And using afterDelay:2
which is kind of a hack may be too short or too long, so how can I implement it?
我不知道在reloadData
. 使用afterDelay:2
哪种 hack 可能太短或太长,那么我该如何实现呢?
采纳答案by Louis
I was recently working with reloadData
-- reloadData
doesn't change the contentOffset
or scroll the table view. It actually stays the same if the offset is less than the new amount of data.
我最近在使用reloadData
--reloadData
不会更改contentOffset
或滚动表格视图。如果偏移量小于新的数据量,它实际上保持不变。
回答by Matt Koala
I was having trouble with this because I mess with cell sizing in my cellForRowAtIndexPath method. I noticed that the sizing information was off after doing reloadData, so I realized I needed to force it to layout immediately before setting the content offset back.
我遇到了这个问题,因为我在 cellForRowAtIndexPath 方法中弄乱了单元格大小。我注意到在执行 reloadData 后大小信息已关闭,所以我意识到我需要在设置内容偏移之前立即强制它进行布局。
CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];
回答by Skywalker
Calling reloadData
on the tableView does not change the content offset. However, if you are using UITableViewAutomaticDimension
which was introduced in iOS 8, you could have an issue.
调用reloadData
tableView 不会更改内容偏移量。但是,如果您使用的UITableViewAutomaticDimension
是 iOS 8 中引入的功能,则可能会遇到问题。
While using UITableViewAutomaticDimension
, one needs to write the delegate method tableView: estimatedHeightForRowAtIndexPath:
and return UITableViewAutomaticDimension
along with tableView: heightForRowAtIndexPath:
which also returns the same.
当使用UITableViewAutomaticDimension
,需要写委托方法tableView: estimatedHeightForRowAtIndexPath:
并返回UITableViewAutomaticDimension
随着tableView: heightForRowAtIndexPath:
这也返回相同。
For me, I had issues in iOS 8 while using this. It was because the method estimatedHeightForRowAtIndexPath:
method was returning inaccurate values even though I was using UITableViewAutomaticDimension
. It was problem with iOS 8 as there was no issue with iOS 9 devices.
对我来说,我在使用它时在 iOS 8 中遇到了问题。这是因为estimatedHeightForRowAtIndexPath:
即使我使用UITableViewAutomaticDimension
. iOS 8 有问题,因为 iOS 9 设备没有问题。
I solved this problem by using a dictionary to store the value of the cell's height and returning it. This is what I did.
我通过使用字典来存储单元格高度的值并返回它来解决这个问题。这就是我所做的。
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *key = @(indexPath.row);
NSNumber *height = @(cell.frame.size.height);
[self.cellHeightsDictionary setObject:height forKey:key];
}
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *key = @(indexPath.row);
NSNumber *height = [self.cellHeightsDictionary objectForKey:key];
if (height)
{
return height.doubleValue;
}
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
The check for whether height exists is for the first time page loads.
高度是否存在的检查是第一次页面加载。
回答by Nik Kov
Swift 4 variant of @Skywalker answer:
@Skywalker 答案的 Swift 4 变体:
fileprivate var heightDictionary: [Int : CGFloat] = [:]
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
heightDictionary[indexPath.row] = cell.frame.size.height
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let height = heightDictionary[indexPath.row]
return height ?? UITableViewAutomaticDimension
}
Another solution (fetched from MessageKit):
另一个解决方案(从 MessageKit 获取):
This method should be called instead of reloadData
. This can fit for specific cases.
应调用此方法而不是reloadData
. 这可以适用于特定情况。
public func reloadDataAndKeepOffset() {
// stop scrolling
setContentOffset(contentOffset, animated: false)
// calculate the offset and reloadData
let beforeContentSize = contentSize
reloadData()
layoutIfNeeded()
let afterContentSize = contentSize
// reset the contentOffset after data is updated
let newOffset = CGPoint(
x: contentOffset.x + (afterContentSize.width - beforeContentSize.width),
y: contentOffset.y + (afterContentSize.height - beforeContentSize.height))
setContentOffset(newOffset, animated: false)
}
回答by Cyril
By default, reloadData keeps the contentOffset. However, it could be updated if you do have inaccurate estimatedRowHeight values.
默认情况下,reloadData 保持 contentOffset。但是,如果您确实有不准确的估计行高度值,则可以更新它。
回答by saranpol
回答by bingxin xue
If you implement estimatedHeightForRowAtIndexPath
method and your estimate is not right, you will possible get into this situation.
如果你执行estimatedHeightForRowAtIndexPath
方法并且你的估计不正确,你可能会陷入这种情况。
To solve this, you can return a large height that bigger than every cell height in your tableView, like this:
为了解决这个问题,您可以返回一个比 tableView 中每个单元格高度都大的大高度,如下所示:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 800.f; // if 800 is bigger than every possible value of your cell height.
}
回答by Bohdan Savych
If you insert data at the beginning of your dataSource array, you need to change contentOffset
like this:
Swift 3+
如果在 dataSource 数组的开头插入数据,则需要contentOffset
像这样更改:
Swift 3+
func prepareReloadData() {
let previousContentHeight = tableView.contentSize.height
let previousContentOffset = tableView.contentOffset.y
tableView.reloadData()
let currentContentOffset = tableView.contentSize.height - previousContentHeight + previousContentOffset
tableView.contentOffset = CGPoint(x: 0, y: currentContentOffset)
}
回答by Mikhail Vasilyev
I had the same issue however none of answers suggested here worked. Here's how i solved it. Subclass UITableView
and override layoutSubviews
method like this:
我遇到了同样的问题,但是这里建议的答案都没有奏效。这是我解决它的方法。子类UITableView
和覆盖layoutSubviews
方法如下:
override func layoutSubviews() {
let offset = contentOffset
super.layoutSubviews()
contentOffset = offset
}
回答by Andrew Romanov
@Skywalker's answer showed best workaround for estimated height of cells problem. But sometimes problem lyes in a different place.
Sometimes the problem lyes in contentInsets of table view. If you make reload data while tableView is not visible on the screen you can face with wrong offset after the table view appears on the screen.
It happens because UIViewController can control insets if his scrollView when the scrollView is appearing to allow lying of scrollView below transparent navigationBar and statusBar.
I've faced with this behaviour in iOS 9.1
@Skywalker 的回答显示了估计单元格高度问题的最佳解决方法。但有时问题所在的地方不同。
有时问题出在表格视图的 contentInsets 中。如果在 tableView 在屏幕上不可见时重新加载数据,则在 tableView 出现在屏幕上后可能会遇到错误的偏移量。
发生这种情况是因为 UIViewController 可以控制插入,如果当滚动视图出现时他的滚动视图允许滚动视图位于透明导航栏和状态栏下方。
我在 iOS 9.1 中遇到过这种行为