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];
私はreloadData
の後に呼び出されるデリゲートメソッドを知らないことを知っています。そして、ハックの一種であるafterDelay:2
を使用するのは短すぎたり長すぎたりする可能性があります。
私は最近reloadData
を使っていました-reloadData
はcontentOffset
を変更せず、テーブルビューをスクロールしません。オフセットが新しいデータ量より少ない場合、実際には同じままです。
CellForRowAtIndexPathメソッドでセルのサイズを変更するため、これで問題が発生していました。 reloadDataの実行後にサイズ情報がオフになっていることに気づいたので、コンテンツオフセットを設定する直前にレイアウトを強制する必要があることに気付きました。
CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];
TableViewでreloadData
を呼び出しても、コンテンツオフセットは変更されません。ただし、iOS 8で導入されたUITableViewAutomaticDimension
を使用している場合は、問題が発生する可能性があります。
UITableViewAutomaticDimension
を使用している間、デリゲートメソッドtableView: estimatedHeightForRowAtIndexPath:
を記述し、tableView: heightForRowAtIndexPath:
とともにUITableViewAutomaticDimension
を返す必要があります。
私にとって、これを使用している間にiOS 8で問題が発生しました。 UITableViewAutomaticDimension
を使用していても、メソッドestimatedHeightForRowAtIndexPath:
メソッドが不正確な値を返していたためです。 iOS 9デバイスでは問題がなかったため、iOS 8では問題でした。
辞書を使用してセルの高さの値を保存して返すことで、この問題を解決しました。これは私がやったことです。
- (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;
}
高さが存在するかどうかのチェックは、ページが初めてロードされるときです。
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
}
このメソッドは、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)
}
デフォルトでは、reloadDataはcontentOffsetを保持します。ただし、不正確なtimatedRowHeight値がある場合は更新される可能性があります。
estimatedHeightForRowAtIndexPath
メソッドを実装し、推定が正しくない場合、この状況に陥る可能性があります。
これを解決するには、次のように、tableViewのすべてのセルの高さよりも大きな大きな高さを返すことができます。
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 800.f; // if 800 is bigger than every possible value of your cell height.
}
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)
}
@Skywalkerの答えは、セルの高さの推定問題に対する最善の回避策を示しました。しかし、時には別の場所で問題を起こします。
テーブルビューのcontentInsetsで問題が解決する場合があります。テーブルビューが画面に表示されていないときにデータをリロードすると、テーブルビューが画面に表示された後に間違ったオフセットに直面する可能性があります。
。
iOS 9.1でこの動作に直面しました
私は同じ問題を抱えていましたが、ここで提案された答えはどれもうまくいきませんでした。ここに私がそれを解決した方法があります。 UITableView
をサブクラス化し、layoutSubviews
メソッドを次のようにオーバーライドします。
override func layoutSubviews() {
let offset = contentOffset
super.layoutSubviews()
contentOffset = offset
}