以下是尝试双粘性标题视图的完整代码示例:
struct ContentView: View {
var body: some View {
ScrollView(showsIndicators: false) {
LazyVStack(spacing: 0, pinnedViews: .sectionHeaders) {
Section {
SecondView()
} header: {
HeaderView()
}
}
}
}
}
struct HeaderView: View {
var body: some View {
Rectangle()
.fill(.green)
.frame(height: 50)
}
}
struct SecondView: View {
var body: some View {
LazyVStack(spacing: 0, pinnedViews: .sectionHeaders) {
Section {
VStack {
ForEach(0..<20) { index in
ItemView(index: index)
}
}
} header: {
SecondHeaderView()
}
}
}
}
struct SecondHeaderView: View {
var body: some View {
Rectangle()
.fill(.red)
.frame(height: 60)
}
}
struct ItemView: View {
let index: Int
var body: some View {
VStack {
Text("Item \(index)")
.padding()
}
.frame(height: 80)
.background(.gray)
}
}
#Preview {
ContentView()
}
请注意,最初,红色的SecondHeaderView
正确位置位于绿色的下方HeaderView
。但是,滚动时,红色标题会滚动到绿色标题下方,直到它停留在与绿色标题相同的 y 位置。
我需要红色标题固定在绿色标题下方。请注意,SecondView
可能并不总是嵌入在中ContentView
。它可以独立存在,但仍然需要自己的单个粘性标题才能工作,因此我认为两个视图都需要LazyVStack
。
我弄乱了各种 GeometryReader 视图偏移,试图让红色标题正确定位,但我无法做到正确。
以下是两种可能的解决方法
1. 将标题合并在一起
一个简单的解决方法是将两个标题合并在一起。如果有可能
SecondView
单独显示,那么您可以传递一个标志,以指示是否应显示标题:SecondView
单独显示:2. 对第二个 header 应用 padding
或者,如果可以假设第二个标题在最初显示时处于正确位置,则可以用 来测量该位置
.onAppear
。然后,为了将其保持在同一位置,可以应用顶部填充来补偿任何滚动移动。必须将相同的填充作为负顶部填充应用于滚动内容,否则内容不会移动,直到拖动移动达到第一个标题的高度。
我发现滚动时标题会做出微小的移动,这会导致控制台出现“操作尝试每帧更新多次”的错误。可以通过检查调整与之前的量是否相差阈值来防止这些错误,0.1 就可以了。
通过这种方法,
SecondView
可以单独显示而不需要传递标志。如果不能假设第二个标题在初始显示时处于正确位置,则可以将父标题的高度作为参数传递。在使用填充方法时,这可能是一种更安全的解决方法:
两种方法的工作原理相同。双头的情况如下: