Я хочу, чтобы заголовки прикреплялись к верху при вертикальной прокрутке.
Код примерно такой:
ScrollView([.vertical]) {
LazyVStack(alignment: .leading, pinnedViews: .sectionHeaders) {
ForEach(...) {
ScrollView([.horizontal]) {
LazyVStack(spacing: 0, pinnedViews: .sectionHeaders) {
Section {
...
} header: {
... // <-- the header
}
}
}
}
}
}
Однако, кажется, что in nested scroll views, the pinnedViews property can only pin views in the nearest scroll view
.
Наконец-то разберитесь с проблемой.
Включая некоторые ответы, такие как https://stackoverflow.com/a/71154712/9073579, я в итоге использовал один ScrollView для достижения этой функциональности.
Этот подход также создает проблему, заключающуюся в том, что представление центрируется, когда высоты внутреннего содержимого недостаточно.
Таким образом, мне пришлось потратить дополнительные усилия на решение этого вопроса. Это потребовало дополнительных переменных для поддержания некоторого состояния, таких как ширина всей области прокрутки и расчетная ширина элементов, которые должны быть закреплены.
@State private var width: CGFloat? = nil
var body: some View {
ScrollViewReader { proxy in
GeometryReader { geometry in // <-- to get the viewport size
ScrollView([.vertical, .horizontal]) {
ZStack(alignment: .topLeading) { // to place the scroll anchor
VStack(alignment: .leading) {
VStack(alignment: .leading) {
// my headers view here
}
.stickyHorizontal(geometry.size.width, scrollWidth: width)
ForEach(0..<2) { i in
LazyVStack(alignment: .leading, spacing: 0, pinnedViews: .sectionHeaders) {
Section {
... // body table view
} header: {
... // table header
// calculating the width of header (i.e. the width of table)
.background(GeometryReader { geometry in
Color.clear
.onAppear {
width = geometry.size.width
}
.onChange(of: geometry.size.width) { newValue in
width = newValue
}
})
}
}
}
// addtional views (footer)
...
// act as a Spacer(). Stretching the VStack.
Color.clear
.frame(height: geometry.size.height - 40)
}
.frame(width: width)
// scroll destination
Color.clear
.frame(width: 1, height: 1)
.id("topLeading")
}
.onAppear {
// make sure the scroll view appears from top leading.
proxy.scrollTo("topLeading", anchor: .topLeading)
}
}
}
}
}
extension View {
@ViewBuilder
func stickyHorizontal(_ viewPortWidth: CGFloat, scrollWidth: CGFloat?) -> some View {
LazyHStack( spacing: 0, pinnedViews: .sectionHeaders) {
Section {
Color.clear
.frame(width: scrollWidth == nil ? nil : scrollWidth! - viewPortWidth)
} header: {
self
.frame(width: viewPortWidth)
}
}
}
}