To keep a section header fixed at the top of a SwiftUI view, you can use the following steps:
Create a parent scrollView
that will contain the section header and the child scrollView
.
Inside the parent scrollView
, add the section header as the first item, followed by the child scrollView
.
Apply a fixed height to the section header, which will determine its size and position.
Add a GeometryReader
to the parent scrollView
to capture its dimensions.
Create a State
variable to store the parent scrollView
offset.
Add a .onPreferenceChange
modifier to the child scrollView
that sends its offset to the parent scrollView
through a PreferenceKey
.
Use the parent scrollView
offset to adjust the position of the section header.
Here's an example implementation:
struct ContentView: View {
@State private var parentScrollViewOffset: CGFloat = 0
var body: some View {
ScrollView {
GeometryReader { geometry in
VStack {
SectionHeader()
.frame(height: 50)
.offset(y: max(-geometry.safeAreaInsets.top, -parentScrollViewOffset))
ChildScrollView()
.background(GeometryReader {
Color.clear.preference(key: OffsetPreferenceKey.self,
value: $0.frame(in: .global).minY)
})
.onPreferenceChange(OffsetPreferenceKey.self) { offset in
parentScrollViewOffset = offset
}
}
}
}
}
}
struct SectionHeader: View {
var body: some View {
Text("Header")
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.background(Color.blue)
}
}
struct ChildScrollView: View {
var body: some View {
ScrollView {
ForEach(1..<100) { index in
Text("Item \(index)")
.frame(height: 50)
.background(Color.gray.opacity(0.2))
}
}
}
}
// Preference key to capture the child scrollView offset
struct OffsetPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
Asked: 2023-05-22 14:49:11 +0000
Seen: 12 times
Last updated: May 22 '23