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