SwiftUI ScrollView ScrollTo Offset (NOT ID)

1.3k Views Asked by At

Is it possible to SET ScrollView offset in SwiftUI?

I have made a custom tab bar that uses a Switch/Case to change views. However my views all contain vertical ScrollViews. I understand that each time I switch between Views they are destroyed, and thus the scrollView offset is lost.

I have used the following approach to GET ScrollView Offset, however I am now not sure how I can use this information. I have seen there is now ScrollTo but this seems to only work with an ID.

Is it possible to use ScrollTo with an Offset in some way?

In general, what I'm trying to achieve is standard Tab bar behaviour where a user returns to the same position they left each Tab

Any help is appreciated. Also, please let me know if this is bad for performance as I am a novice. Thanks.

private struct ScrollViewOffsetPreferenceKey: PreferenceKey {
    static var defaultValue: CGPoint = .zero
    static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) { }
}

struct ScrollViewWithOffset<T: View>: View {
    let axes: Axis.Set
    let showsIndicator: Bool
    let offsetChanged: (CGPoint) -> Void
    let content: T
    
    init(axes: Axis.Set = .vertical,
         showsIndicator: Bool = false,
         offsetChanged: @escaping (CGPoint) -> Void = { _ in },
         @ViewBuilder content: () -> T
    ) {
        self.axes = axes
        self.showsIndicator = showsIndicator
        self.offsetChanged = offsetChanged
        self.content = content()
    }
    
    var body: some View {
        ScrollView(axes, showsIndicators: showsIndicator) {
            GeometryReader { proxy in
                Color.clear.preference(
                    key: ScrollViewOffsetPreferenceKey.self,
                    value: proxy.frame(in: .named("scrollView")).origin
                )
            }
            .frame(width: 0, height: 0)
            content
        }
        .coordinateSpace(name: "scrollView")
        .onPreferenceChange(ScrollViewOffsetPreferenceKey.self, perform: offsetChanged)
    }
}

Used like so...

ScrollViewWithOffset { point in
    scrollViewOffset = point.y
} content: {
    ScrollViewReader { proxy in
        LazyVStack(spacing: 4) {
            ForEach(0..<10, id: \.self) { i in
                Item()
                    .id(i)
            }
        }
    }
}
0

There are 0 best solutions below