はじめに
今作っているアプリで、Instagramのように写真をページングで表示したいと思い、SwiftUIのCustomViewを作ってみました。
SwiftUIのScrollViewのscrollイベントを検知する方法や、ScrollViewをprogrammaticallyにscrollする方法を見つけるのに苦戦したので、同じことをしたい人にとって きっと役に立つと思います。
完成イメージ
E7系、かっこいい
ソース
画像、"e7_square","e5_square","doctor_yellow_square"は、お手持ちの画像に置き換えてください。
ImagePager.swift
import SwiftUI
struct ImagePager: View {
var imageNames: [String]
@State private var index: Int = 0
@State private var offset: CGFloat = 0
var body: some View {
GeometryReader { geometry in // 1. offset(scroll位置)を操作するため、GeometryReaderを利用
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 0) {
ForEach(0..<self.imageNames.count) {
Image(self.imageNames[$0])
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: geometry.size.width, height: geometry.size.width)
}
}
}
.content.offset(x: self.offset) // 2. self.offsetとscrollViewのoffsetをバインディング
.frame(width: geometry.size.width, height: nil, alignment: .leading)
.gesture(DragGesture()
.onChanged({ value in // 3. Dragを監視。Dragに合わせて、スクロールする。
self.offset = value.translation.width - geometry.size.width * CGFloat(self.index)
})
.onEnded({ value in // 4. Dragが完了したら、Drag量に応じて、indexを更新
let scrollThreshold = geometry.size.width / 2
if value.predictedEndTranslation.width < -scrollThreshold {
self.index = min(self.index + 1, self.imageNames.endIndex - 1)
} else if value.predictedEndTranslation.width > scrollThreshold {
self.index = max(self.index - 1, 0)
}
withAnimation { // 5. 更新したindexの画像をアニメーションしながら表示する。
self.offset = -geometry.size.width * CGFloat(self.index)
}
})
)
}
}
}
struct Carousel_Previews: PreviewProvider {
static var previews: some View {
ImagePager(imageNames: [
"e7_square",
"e5_square",
"doctor_yellow_square"
])
}
}
解説
- offset(scroll位置)を操作するため、GeometryReaderを利用
- self.offsetとscrollViewのoffsetをバインディング
- Dragを監視。Dragに合わせて、スクロールする。
- Dragが完了したら、Drag量に応じて、indexを更新
- 更新したindexの画像をアニメーションしながら表示する。
参考
以上です。