kitoko552.memo

kitoko552のメモ

UICollectionViewやUITableViewのスクロール処理はViewが描画されてから行う

要点

  • UICollectionView, UITableViewのスクロールはsetContentOffsetscrollToItemAtIndexPath
  • これらの処理はViewが描画されてから呼ばなければいけない。
override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  tableView.setContentOffset(offset, animated: false)
}
  • 以下の記事を参考にしています。

stackoverflow.com

UICollectionView, UITableViewのスクロール処理

UICollectionViewやUITableViewをコードでスクロールしたいときがあります。

例えば、自作のImagePicker。

デフォルトで入っている写真アプリのような画面をイメージしてください。

最新の写真は一番下にあるため、遷移してきたときに一番下までスクロールさせたいですね。

例えば、チャットアプリ。

LINEのようなアプリをイメージしてください。

最新のメッセージのやりとりが一番下にあるとすると、これも遷移してきたときに一番下までスクロールさせたいです。

こんな感じで、UICollectionViewやUITableViewをコードでスクロールしたい場面は割とあります。

setContentOffsetかscrollToItemAtIndexPathを使う

僕が実際に使ったのはsetContentOffsetです。

ですが、scrollToItemAtIndexPathでもスクロールできると思います(試してませんが)。

これらは二つともUICollectionView, UITableViewのメソッドです。

これらを使えばスクロールさせることは簡単にできます。

メソッドを呼ぶのはViewが描画されてから

しかし注意点があります。

それは、これらのメソッドはそれぞれのViewが描画されてから呼ばなければいけないということです。

一応付け加えておくと、それぞれのViewというのはUICollectionViewまたはUITableViewのことです。

Viewが描画される前に「ここまでスクロールしてくれ」と言われても、Viewはその「ここ」がわかりません。

そのため、Viewが描画される前に上のメソッドを呼んでも何も起きません。

viewDidAppearかviewDidLayoutSubviews内で呼ぶ

なので、viewDidLoadviewWillAppearでは呼ぶべきではないでしょう。

これらの中で呼ぶとほぼViewが描画される前にメソッドが呼ばれてしまい、何も起きません(たまたま描画後に呼ばれることはあるかもしれませんが)。

なので、viewDidAppearviewDidLayoutSubviews内で呼ぶことをおすすめします。

viewDidAppear内で呼ぶと、そのView全体が描画してからスクロールするため、一番上にいる状態が見えてからスクロールします。

意図的にスクロールを見せたい場合はviewDidAppear内で呼ぶといいでしょう。

override func viewDidAppear() {
  super.viewDidAppear()
  tableView.setContentOffset(offset, animated: false)
}

一方、viewDidLayoutSubviews内で呼ぶと、スクロールしてから遷移したような見え方になります。

ただし、viewDidLayoutSubviews複数回呼ばれるため、何度も呼んでしまわないような工夫が必要です。

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  if ... { // 一度だけ呼ばれるようにする
    tableView.setContentOffset(offset, animated: false)
  }
}

自作のUICollectionViewやUITableViewを使っているのであれば、ViewControllerに書かずに、自作クラスでviewDidLayoutSubviewsをオーバーライドして書くのもいいでしょう。