【Swift UI】独自カルーセルUIの実装方法!スワイプでコンテンツを切り替え

【Swift UI】独自カルーセルUIの実装方法!スワイプでコンテンツを切り替え

この記事からわかること

  • Swift UIカルーセルUI実装するには?
  • @GestureState使い方
  • ドラッグ(スワイプ)したビュー移動させる方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

カルーセルとは?

カルーセルとはスライドやタップなどによってコンテンツをスライドさせて複数のコンテンツを表示するビューのことです。自動でコンテンツをスライドさせるものはスライドショーなどと呼ばれたりします。

Swift UIでスワイプすることで表示しているコンテンツが切り替わるようなビューを実装してみたので実装方法をまとめておきます。

【Swift UI】独自カルーセルUIの実装方法!スワイプでコンテンツを切り替え

Swift UIで実装してみる

仕組み

【Swift UI】独自カルーセルUIの実装方法!スワイプでコンテンツを切り替え
struct CarouselView: View {
    // カルーセル自体とコンテンツ単体の横幅と高さ
    public var width: CGFloat
    public var height: CGFloat
    
    // 初期表示コンテンツ番号
    @State  private var currentIndex = 1
    // コンテンツ数と内容
    @State  private var pages = [0, 1, 2]
    // スワイプされた際にHStackごとビューを動かす量
    @GestureState  private var dragOffset: CGFloat = 0
    
    var body: some View {
        // 表示
        GeometryReader { geometry in
            HStack(spacing: 0) {
                ForEach(pages, id: \.self) { page in
                    Text("page\(page)")
                        .foregroundColor(.white)
                        .frame(width: width, height: height)
                        .background(page == 1 ? Color.black : Color.gray)
                }
            }
            // X方向にスワイプされた量だけHStackをずらす
            // dragOffsetにはスワイプされている間だけその値が格納されスワイプが終了すると0になる
            .offset(x: dragOffset)
            // スワイプが完了してcurrentIndexが変化すると[currentIndex * width]分だけHStackをずらす
            .offset(x: -CGFloat(self.currentIndex) * width)
            .gesture(
                DragGesture(minimumDistance: 0)
                    // スワイプの変化を観測しスワイプの変化分をHStackのoffsetに反映(スワイプでビューが動く部分を実装)
                    .updating(self.$dragOffset, body: { (value, state, _) in
                        print("X方向にスワイプされた量:\(value.translation.width)")
                        // スワイプ変化量をdragOffsetに反映
                        state = value.translation.width
                        // スワイプが完了するとdragOffsetの値は0になる
                    })
                    .onEnded { value in
                        print("最終的な変化量:\(value.translation.width)")
                        // 最終的な変化量が正数ならcurrentIndexを減らし、負数なららcurrentIndexを増やす
                        let newIndex = value.translation.width > 0 ? self.currentIndex - 1 : self.currentIndex + 1
                        // コンテンツ数を最大/最小のインデックスとする(-1や2以上にならないように制御)
                        self.currentIndex = max(0, min(2, newIndex))
                    }
            )
        }// アニメーションをなめらかに
        .animation(.interpolatingSpring(mass: 0.6, stiffness: 150, damping: 80, initialVelocity: 0.1))
        .frame(width: width, height: height)
        .clipped()
    }
}

実際に使用する際は以下のように使用します。

struct ContentView: View {
    var body: some View {
        CarouselView(width: 300, height: 200)
    }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index