I'm trying to create sticky header with manual scroll to functionality (eg. categories bar allowing user to move to chosen section). I have issues with one part of that functionallity. While ScrollViewReader and proxy.scrollTo works as intended; it keeps scrolling under sticky header instead of below it. Image one shows how it works, image two shows what is expected. As you can see swiftUI basically ignores existence of header, treating it as separate Z layer.
I would love solution to work on iOS 15.
Code to reproduce example:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
ScrollViewReader(content: { proxy in
ScrollView {
LazyVStack(spacing: 0, pinnedViews: [.sectionHeaders]) {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
.onTapGesture {
proxy.scrollTo(sections[2].hashValue, anchor: .top)
}
Section {
NestedSectionsView()
.frame(maxWidth: .infinity)
} header: {
Header().opacity(0.5)
}
}
}
})
}
.padding()
}
}
#Preview {
ContentView()
}
let sections = [
SectionData(header: "First Section", items: Array(repeating: "Item 1", count: 5)),
SectionData(header: "Second Section", items: Array(repeating: "Item 2", count: 5)),
SectionData(header: "Third Section", items: Array(repeating: "Item 3", count: 10))
]
struct NestedSectionsView: View {
var body: some View {
LazyVStack(spacing: 10) {
ForEach(sections, id: \.hashValue) { section in
SectionView(section: section)
}
}
.padding()
.navigationBarTitle("Nested Sections")
}
}
struct SectionData: Identifiable, Hashable {
let id = UUID()
let header: String
let items: [String]
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
struct SectionView: View {
let section: SectionData
var body: some View {
VStack(alignment: .leading, spacing: 3) {
Text(section.header)
.font(.headline)
.frame(height: 50)
.frame(maxWidth: .infinity)
.background(Color.green)
.id(section.hashValue)
ForEach(section.items, id: \.self) { item in
Text(item)
.frame(height: 140)
.frame(maxWidth: .infinity)
.background(Color.red)
}
}
}
}
struct Header: View {
var body: some View {
VStack {
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: UIScreen.main.bounds.width, height: 100)
.padding()
Text("Example Text")
.font(.title)
.foregroundColor(.black)
.frame(width: UIScreen.main.bounds.width, height: 100, alignment: .center)
}.background(Color.yellow)
}
}
How does it work now (when tapping scroll to id of section 3)

How does I would like it to work (scrolls so section title is visible, and not covered by sticky header)

