SwiftUIのListは、UIKitのTableViewよりも簡単にリストを構築できるようになって嬉しいです。
しかし、offsetが今までのように簡単に取得できなかったので、カスタムListを作ってみました。
使い方
struct ContentView: View {
@State private var offset: CGFloat = 0
var body: some View {
TrackableList(contentOffset: $offset) {
ForEach(0..<100) { _ in
Text("offset:\(self.offset)")
}
}
}
}
Offset取得可能なカスタムListを作る
struct TrackableList<Content>: View where Content: View {
@Binding var contentOffset: CGFloat
let content: Content
init(contentOffset: Binding<CGFloat>, @ViewBuilder content: () -> Content) {
self._contentOffset = contentOffset
self.content = content()
}
var body: some View {
GeometryReader { outsideProxy in
List {
ZStack {
GeometryReader { insideProxy in
Color.clear
.preference(key: ScrollOffsetPreferenceKey.self, value: [outsideProxy.frame(in: .global).minY - insideProxy.frame(in: .global).minY])
// Send value to the parent
}
VStack {
self.content
}
}
}
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
self.contentOffset = value[0]
}
// Get the value then assign to offset binding
}
}
}
struct ScrollOffsetPreferenceKey: PreferenceKey {
typealias Value = [CGFloat]
static var defaultValue: [CGFloat] = [0]
static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
value.append(contentsOf: nextValue())
}
}
Viewの外側と内側にそれぞれGeometryReader
を置き、その差し分を計算することでoffsetを取得しています。
参考
こちらの記事を参考にしました。
以下記事では、Listではなく、ScrollViewのoffsetを取得しています。
horizontal
、vertical
やshowIndicators
などの設定もできるようになっています。
https://medium.com/@maxnatchanon/swiftui-how-to-get-content-offset-from-scrollview-5ce1f84603ec