Skip to content

Instantly share code, notes, and snippets.

@insidegui
Created July 20, 2021 20:28
Show Gist options
  • Save insidegui/8dfac0b52a70bb4c4513934672d3093c to your computer and use it in GitHub Desktop.
Save insidegui/8dfac0b52a70bb4c4513934672d3093c to your computer and use it in GitHub Desktop.
A SwiftUI ViewModifier that can be used to read a ScrollView's offset and store it into a @State property of the view
struct ScrollViewOffsetPreferenceKey: PreferenceKey {
static var defaultValue: CGPoint = .zero
static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) {
value = nextValue()
print("value = \(value)")
}
typealias Value = CGPoint
}
struct ScrollViewOffsetModifier: ViewModifier {
let coordinateSpace: String
@Binding var offset: CGPoint
func body(content: Content) -> some View {
ZStack {
content
GeometryReader { proxy in
let x = proxy.frame(in: .named(coordinateSpace)).minX
let y = proxy.frame(in: .named(coordinateSpace)).minY
Color.clear.preference(key: ScrollViewOffsetPreferenceKey.self, value: CGPoint(x: x * -1, y: y * -1))
}
}
.onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in
offset = value
}
}
}
extension View {
func readingScrollView(from coordinateSpace: String, into binding: Binding<CGPoint>) -> some View {
modifier(ScrollViewOffsetModifier(coordinateSpace: coordinateSpace, offset: binding))
}
}
// Sample usage:
struct CarouselView: View {
let items = (0..<10).map({ $0 })
@State var offset: CGPoint = .zero
var body: some View {
VStack {
Text("Offset: \(offset.x)")
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 16) {
ForEach(items, id: \.self) { _ in
RoundedRectangle(cornerRadius: 12, style: .continuous)
.frame(width: 90, height: 90)
.foregroundColor(.blue)
}
}
.readingScrollView(from: "scroll", into: $offset)
}
.coordinateSpace(name: "scroll")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment