A better way to read ScrollView content offset in SwiftUI

Viewed 1683

I'm trying to do this:

  • There is a ScrollView that contains another view at the very top and, possibly, other views below;
  • When user pulls the ScrollView down beyond the top edge, I want that element to stay "glued" to the top edge of the ScrollView;
  • Otherwise, it should scroll normally.

I'm now proficient enough in SwiftUI to implement this behavior thusly:

import SwiftUI

struct ContentView: View {
    @State private var topOffset: CGFloat = 0
    
    struct TopOffsetKey: PreferenceKey {
        typealias Value = CGFloat

        static var defaultValue: Value = 0

        static func reduce(
            value: inout Value,
            nextValue: () -> Value
        ) {
            value = nextValue()
        }
    }
    
    var body: some View {
    
        ScrollView {
            
            VStack {
                
                Rectangle()
                    .fill(Color.red)
                    .aspectRatio(1, contentMode: .fill)
                    .offset(y: topOffset > 0 ? -topOffset : 0)
                    .background(
                        GeometryReader { g in
                            Color.clear
                                .preference(key: TopOffsetKey.self, value: g.frame(in: .named("ScrollView")).origin.y)
                        }
                    )
                
                ForEach(0..<10) { _ in
                    Rectangle()
                        .fill(Color.gray)
                        .frame(height: 100)
                }
                
            }
            
        }
        .coordinateSpace(name: "ScrollView")
        .onPreferenceChange(TopOffsetKey.self) {
            topOffset = $0
        }
        
    }
}


struct ContentView_Previews: PreviewProvider {
        
    static var previews: some View {
        return ContentView()
    }
}

The code works but it feels dirty and my question to the masters is: "Is there a better way to do this?".

Cheers,
–Baglan

0 Answers
Related