Estou tentando criar uma ScrollView com um LazyVStack onde dois cabeçalhos são fixados no topo. Também deve funcionar dentro de um NavigationStack usando o modo de exibição grande para o título. Ambos os cabeçalhos devem estar dentro da ScrollView para que a animação do título grande funcione durante a rolagem (passando do título grande para o pequeno ao rolar para baixo).
Foi isso que eu tentei:
struct StickyHeaderHeightKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
struct ContentView: View {
let scrollCoordinateSpace = "scroll"
@State private var headerHeight: CGFloat = 0
var body: some View {
NavigationStack {
ScrollView {
VStack(alignment: .leading, spacing: 0) {
GeometryReader { geometry in
let frame = geometry.frame(in: .named(scrollCoordinateSpace))
let offset = max(0, -frame.minY)
HStack {
Image(systemName: "pin.fill")
Text("Sticky Header")
.font(.headline)
Spacer()
}
.padding()
.background(Color.red.opacity(0.8))
.offset(y: offset)
.background(
GeometryReader { proxy in
Color.clear
.preference(key: StickyHeaderHeightKey.self, value: proxy.size.height)
}
)
}
.frame(height: headerHeight)
.zIndex(1)
// --- Main Content ---
LazyVStack(spacing: 0, pinnedViews: .sectionHeaders) {
ForEach(0..<50) { index in
Section {
VStack(alignment: .leading, spacing: 0) {
Text("Content \(index + 1)")
.padding(.vertical, 10)
.padding(.horizontal)
Text("Content \(index + 1)")
.padding(.vertical, 10)
.padding(.horizontal)
Text("Content \(index + 1)")
.padding(.vertical, 10)
.padding(.horizontal)
}
.background(Color.green.opacity(0.5))
} header: {
VStack(spacing: 0) {
Text("Header")
.padding(.vertical, 6)
.padding(.horizontal)
.background(Color.blue.opacity(0.5))
}
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.blue.opacity(0.5))
}
}
}
}
}
.coordinateSpace(name: scrollCoordinateSpace)
.navigationTitle("Large Title")
.navigationBarTitleDisplayMode(.large)
.onPreferenceChange(StickyHeaderHeightKey.self) { value in
headerHeight = value
}
}
}
}
O problema que estou enfrentando é que os cabeçalhos da seção LazyVStack são sempre fixados na posição mais alta do ScrollView e não abaixo do cabeçalho vermelho fixo.