スクロール位置を指定するScrollViewReaderの使い方を解説します。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.4
【iOS】14.5
【macOS】Big Sur バージョン 11.5
基本的な使い方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
struct ContentView: View { @State private var jumpTo = "" var body: some View { ScrollViewReader { scrollProxy in // ①ScrollViewProxyインスタンスを取得 VStack { ScrollView { VStack { ForEach(1..<100) { Text("\($0) 行目").font(.title) .id($0) // ②スクロール先を指定する為の一意のIDを指定 } } } TextField("飛び先の番号を指定", text: $jumpTo, onCommit: { withAnimation { /// ③scrollToメソッドで飛び先を指定 scrollProxy.scrollTo(Int(jumpTo)) } }) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() } } } } |
①ScrollViewProxyインスタンスを取得
スクロール制御したい箇所をScrollViewReaderで囲みます。
これによりスクロールを制御する為のScrollViewProxyインスタンス(scrollProxy)が取得できます。
②スクロール先を指定する為の一意のIDを指定
ScrollViewの各要素に.id()モディファイアを使って一意のIDを割り当てます。
③scrollToメソッドで飛び先を指定
①で取得したプロキシのscrollTo関数を使って指定されたIDを持つ要素(View)が表示されるまでスクロールします。例のようにwithAnimationで囲むと、アニメーションを伴ってスクロールします。
scrollTo関数の仕様は次の通りです。
1 2 3 |
scrollTo(id, anchor: UnitPoint) |
【引数】
id
スクロールして表示させる要素(View)のIDを指定します。
anchor
対象の要素(View)をどの位置までスクロールさせるかUnitPointで指定します。
例えば .center を使用すると、対象の要素が中央部に配置されるまでスクロールします。Viewがそこまでスクロールできない場合は、最大限可能な所までスクロールします。
未指定(nil)の場合、対象の要素全体が見えるように最小量だけスクロールされます。
全方向スクロールの例
ScrollViewReaderはScrollViewの全方向スクロールにも対応しています。
次の例は、指定した要素(View)を画面の中央に表示するよう、anchorに.centerを指定した例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
struct ContentView: View { @State private var jumpTo = "" var body: some View { ScrollViewReader { scrollProxy in // ①ScrollViewProxyインスタンスを取得 VStack { ScrollView([.vertical, .horizontal]) { VStack(spacing: 10) { ForEach(0..<20) { y in HStack(spacing: 10) { ForEach(1..<6) { x in let index = y * 5 + x Text("\(index)").font(.title) .frame(width: 100, height: 100) .background(Color.green) .id(index) // ②スクロール先を指定する為の一意のIDを指定 } } } } } TextField("飛び先の番号を指定", text: $jumpTo, onCommit: { withAnimation { /// ③scrollToメソッドで飛び先を指定 scrollProxy.scrollTo(Int(jumpTo), anchor: .center) } }) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() } } } } |
Listでの使用
ScrollViewReaderによるスクロール制御はListでも有効です。
ただし、ScrollViewとは若干動きが異なり、anchor に指定できるのは、上下の端と中央のみとなります。(不具合?)
次の例では、anchor に UnitPoint(x: 0.5, y: 0.25) を指定していますが、指定した要素は画面の上から25%の位置ではなく、中央に移動します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
struct ContentView: View { @State private var jumpTo = "" var body: some View { ScrollViewReader { scrollProxy in // ①ScrollViewProxyインスタンスを取得 VStack { List { ForEach(1..<100) { Text("\($0) 行目").font(.title) .id($0) // ②スクロール先を指定する為の一意のIDを指定 } } TextField("飛び先の番号を指定", text: $jumpTo, onCommit: { withAnimation { /// ③scrollToメソッドで飛び先を指定 scrollProxy.scrollTo(Int(jumpTo), anchor: UnitPoint(x: 0.5, y: 0.25)) } }) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() } } } } |