😊

(SwiftUI)ScrollViewで任意の座標にスクロールする

2024/06/14に公開

はじめに

以下の環境で動作確認しました。

  • Xcode Version 14.2 (14C18)
  • iOS 15.7.1
  • 実機 iPhone 13

※iOS 14以上であれば同じ挙動になるはずですが、すべての環境で動作を保証するものではありません。

実装例

縦横が1000 x 1000のフレームを作成し、四隅にボタンを配置します。それぞれのボタンを押すと目的地の座標にスクロールします。

import SwiftUI

struct ContentView: View {
  var body: some View {
    VStack {
      ScrollViewReader { proxy in
        ScrollView([.horizontal, .vertical], showsIndicators: true) {
          ZStack {
            Button(action: {
              withAnimation {
                proxy.scrollTo(0, anchor: UnitPoint(x: 0.0, y: 1.0))
              }
            }) {
              Text("左下に移動")
                .frame(width: 250, height: 250)
                .foregroundColor(.white)
                .background(.indigo)
            }
            .offset(x: 0, y: 0)
            Button(action: {
              withAnimation {
                proxy.scrollTo(0, anchor: UnitPoint(x: 1.0, y: 0.0))
              }
            }) {
              Text("右上に移動")
                .frame(width: 250, height: 250)
                .foregroundColor(.white)
                .background(.indigo)
            }
            .offset(x: 0, y: 750)
            Button(action: {
              withAnimation {
                proxy.scrollTo(0, anchor: UnitPoint(x: 1.0, y: 1.0))
              }
            }) {
              Text("右下に移動")
                .frame(width: 250, height: 250)
                .foregroundColor(.white)
                .background(.indigo)
            }
            .offset(x: 750, y: 0)
            Button(action: {
              withAnimation {
                proxy.scrollTo(0, anchor: UnitPoint(x: 0.0, y: 0.0))
              }
            }) {
              Text("左上に移動")
                .frame(width: 250, height: 250)
                .foregroundColor(.white)
                .background(.indigo)
            }
            .offset(x: 750, y: 750)
          }
          .frame(width: 1000, height: 1000, alignment: .topLeading)
          .border(.blue, width: 10)
          .id(0)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
      }
    }
  }
}

解説

実装の流れは以下のとおりです。

  1. ScrollViewReaderでScrollViewを囲みます。
  2. スクロール制御対象のビューに.id()でIDを設定します。
  3. proxy.scrollTo(ビューのID, anchor: UnitPoint(x: 移動先のx座標, y: 移動先のy座標))を実行して目的の場所にスクロールします。

ScrollViewReaderの中でビューを特定できればIDの値は何でも構いません。実装例ではid(0)としました。

任意の座標にスクロールする場合はUnitPointで移動先の座標を指定します。座標は0.0から1.0に正規化されるので注意してください。左上が(x, y) = (0.0, 0.0)、右下が(x, y) = (1.0, 1.0)です。

実装例のフレームサイズは1000 x 1000ですから、例えばフレームの中心に移動したければUnitPoint(x: 0.5, y: 0.5)を指定します。UnitPoint(x: 500.0, y: 500.0)は間違いです。

参考資料

  1. ScrollViewReader - Apple Developer Documentation
  2. ScrollViewProxy - Apple Developer Documentation
  3. UnitPoint - Apple Developer Documentation

Discussion