17
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

UITableViewのスクロール位置を戻したい!

Last updated at Posted at 2020-10-12

Xcode-12.0 iOS-14.0 Swift-5.3

はじめに

UITableViewreloadData() をしたときにトップへスクロールしたいときがたまにあります。でもこれが結構めんどくさい。。。
色々方法を試したので備忘録として記載します。

やりたいことは「ボタン押下したときにリロード+スクロール位置をトップに戻したい」ということ!

こんな感じ(before が最下部表示で after が before の状態からリロード+トップへスクロールした状態です)

before after
before after

テーブルは前回の記事で書いたヘッダーが消えたり出たりする grouped スタイルのテーブルでやります!
(before はヘッダー非表示、after はヘッダー表示です)

方法

テーブルをトップにスクロールする方法として setContetnOffsetscrollToRow があり、 reloadData の前にスクロールさせるのか後にスクロールさせるのかで4パターンできると思います。

実行結果1

実装はこんな感じ

// パターン1(setContetnOffset先スクロール)
tableView.setContentOffset(.zero, animated: false)
tableView.reloadData()

// パターン2(scrollToRow先スクロール)
tableView.scrollToRow(at: .init(row: 0, section: 0), at: .top, animated: false)
tableView.reloadData()

// パターン3(setContetnOffset後スクロール)
tableView.reloadData()
tableView.setContentOffset(.zero, animated: false)

// パターン4(scrollToRow後スクロール)
tableView.reloadData()
tableView.scrollToRow(at: .init(row: 0, section: 0), at: .top, animated: false)

結果はこんな感じ

パターン1 パターン2 パターン3 パターン4
1 2 3 4

パターン1とパターン4(でも row 設定なのでヘッダーまではスクロールしてない)はいけてそうだけどパターン2、3が中途半端な位置になっている。。。

実行結果2

なんかわからんけどとりあえずコンテンツサイズが確定してない=レイアウトが中途半端な状態だから上のような結果になるんだろうと思い layoutIfNeeded() 呼べばいいんじゃね?ということで下記のように実装してみた。

// パターン1(setContetnOffset先スクロール)
tableView.setContentOffset(.zero, animated: false)
tableView.layoutIfNeeded()
tableView.reloadData()

// パターン2(scrollToRow先スクロール)
tableView.scrollToRow(at: .init(row: 0, section: 0), at: .top, animated: false)
tableView.layoutIfNeeded()
tableView.reloadData()

// パターン3(setContetnOffset後スクロール)
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.setContentOffset(.zero, animated: false)

// パターン4(scrollToRow後スクロール)
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.scrollToRow(at: .init(row: 0, section: 0), at: .top, animated: false)

結果はこんな感じ

パターン1 パターン2 パターン3 パターン4
l_1 l_2 l_3 l_4

全パターンいい感じにいけてそう:tada:

実行結果3

layoutIfNeeded() を呼ぶことでそれぞれ思った通りの動作になってますがあまいです!セルの高さが固定なら問題ないのですがセルの高さが可変の場合はちょっと。。。:confounded:

下記のような2パターンのセルを用意します。

cell

実行結果2の処理を試してみると結果はこんな感じ(ちなみに layoutIfNeeded ないパターンだとパターン4以外は中途半端なスクロールになりました)

パターン1 パターン2 パターン3 パターン4
b_l_1 b_l_2 b_l_3 b_l_4

パターン3以外はいけてそうです:clap:おそらく tableView(_ :estimatedHeightForRowAt:) で適切な値を返してやるとパターン3でもうまくいくと思います。2パターンとかならいいですがここにオートレイアウトとか関与してくると色々高さ計算がめんどくさくなります。。。

セルの高さに関しては下記の記事に色々丁寧に記載されていたので参考に(私はあんまりわかってない。。。)
UITableViewのrowHeightやestimatedRowHeightに何を設定すると良いのか

結論

今回は計算とかいろいろめんどくさいのとヘッダーまでスクロールしたかったので下記を採用しました。(たぶんいけてそう:v:

// パターン1(setContetnOffset先スクロール)
tableView.setContentOffset(.zero, animated: false)
tableView.layoutIfNeeded()
tableView.reloadData()

動作はこんな感じ

scroll

20201024追記

コメントでいただきましたが下記のようにスクロール位置を .top から .bottom に変更するといけそうです:tada:

tableView.reloadData()
tableView.scrollToRow(at: .init(row: 0, section: 0), at: .bottom, animated: false)

layoutIfNeeded も必要ないですしこれが一番いいんじゃないかなと思います:relaxed:

おわりに

reloadData とトップへスクロールの組み合わせはわりとやることあるんですが結局どういう方法がいいのかな?といつも悩んでしまいます。。。

下記のようなやり方もあるみたいです。

iOS TableView reload and scroll top

他なにかいい方法ご存知であればぜひご教授ください:raised_hands:

17
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?