🐵
[UIKit] 画面表示前にscrollToItem(at:at:animated:)したい!
Swift/Kotlin愛好会アドベントカレンダー19日目の記事です。
はじめに
CollectionViewが載っている画面を表示前に指定箇所へスクロールする際に少し工夫が必要だったので、自分なりの実装を書いておきます。
画面構成
A画面:B画面に遷移するボタンがある
B画面:CollectionViewが載っている
実装
AViewController
class AViewController: UIViewController {
@IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
//B画面へIndexPathを渡して遷移するアクションを追加
button.addAction(UIAction(handler: { _ in
let bVC = self.storyboard!.instantiateViewController(identifier: "B") { corder in
return BViewController(coder: corder, initialIndexPath: IndexPath(row: 20, section: 0))
}
self.present(bVC, animated: true)
}), for: .touchUpInside)
}
}
BViewController
class BViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private var initialIndexPath: IndexPath?
init?(coder: NSCoder, initialIndexPath: IndexPath) {
self.initialIndexPath = initialIndexPath
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//後に説明
if let initialIndexPath = initialIndexPath {
collectionView.scrollToItem(at: initialIndexPath, at: .centeredVertically, animated: false)
guard let initialCell = collectionView.cellForItem(at: initialIndexPath) else { return }
if collectionView.visibleCells.contains(where: { $0 == initialCell }) {
self.initialIndexPath = nil
}
}
}
}
説明
まずscrollToItem(at:at:animated:)
ですが、この子はviewDidLayoutSubviews()
以降のライフサイクルでのみ動作するようなので[1]viewDidLayoutSubviews()
内で呼び出してあげています。
viewDidAppear(_:)
でも動作するのですが、表示されてからスクロール処理が走る関係上見た目があまりよくないのでやめました。
またviewDidLayoutSubviews()
は画面初期表示時に複数回呼ばれるのですが、
最初の1、2回目の呼び出しではCollectionView
のレイアウトが完了しておらず、
スクロールが行われない場合があるため、スクロールされたかどうかを判定する処理を追加しています。
guard let initialCell = collectionView.cellForItem(at: initialIndexPath) else { return }
if collectionView.visibleCells.contains(where: { $0 == initialCell }) {
self.initialIndexPath = nil
}
スクロールが正常に行われた場合はinitialIndexPath
にnil
を代入して2度目以降のスクロールが起きないようにしています。
おわりに
上記の処理を利用したアプリをリリースしているのでよかったら使ってみてください:)
参考リンク
scrollToItem(at:at:animated:)
viewDidLayoutSubviews()
-
ちゃんと確認したわけではないので間違っているかもしれないです。。。 ↩︎
Discussion