嵌套在 ScrollView 中的 SwiftUI ForEach 打破了 onPreferenceChange

SwiftUI ForEach nested in ScrollView breaks onPreferenceChange

我正在尝试获取 ScrollView 的当前 滚动值 ,以便在其他视图的其他地方使用。

似乎唯一的方法是使用 GeometryReader 并将其值存储在 PreferenceKey 中,然后由 onPreferenceChange 读取,这样我们就不会改变 onPreferenceChange 的状态渲染期间的视图(禁止)。

一切似乎都正常,直到我在滚动视图中嵌入 ForEach 而不是一些静态视图。然后 onPreferenceChange 停止接收事件。

这是一个 NON 工作示例:


struct ScrollViewPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}

struct ContentView: View {

    var body: some View {

        ScrollView {

            GeometryReader { proxy in
                Color.clear.preference(key: ScrollViewPreferenceKey.self,
                                       value: proxy.frame(in: .named("scrollview")).minY)
            }

            VStack(spacing: 20) {
                ForEach(0..<27) { item in // this is causing the issue
                    Text("Item \(item)")
                }
            } //end vstack
        } //end scrollview
        .coordinateSpace(name: "scrollview")
        .onPreferenceChange(ScrollViewPreferenceKey.self, perform: { value in
            let _ = print(value)
        })

    }//endbody
} //endview

结果没有打印任何东西

用硬编码视图替换 ForEach { } 效果很好:

                Group {
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                }
                Group {
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                }
                Group {
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                    Text("Item x")
                }

打印正确值的结果

0.0 -11.666666666666664 -49.333333333333336 -104.33333333333334 -174.33333333333331 -252.66666666666666 -315.0 ...

我认为滚动视图和动态内容一定存在问题,但如何解决?

即使 ForEach 在层次结构或其他文件中嵌入了多层视图,也会出现此问题。

这只是另一个 SwiftUI 错误还是我没有掌握某些东西(我是框架的新手)?

简单

struct ContentView: View {

    var body: some View {

        ScrollView {

            GeometryReader { proxy in
                Color.clear.preference(key: ScrollViewPreferenceKey.self,
                                       value: proxy.frame(in: .named("scrollview")).minY)
            }

            ForEach(0..<27, id: \.self) { item in // this is causing the issue
                Text("Item \(item)")
                    .padding()
            }
        } //end scrollview
        .coordinateSpace(name: "scrollview")
        .onPreferenceChange(ScrollViewPreferenceKey.self, perform: { value in
            let _ = print(value)
        })

    }//endbody
} //endview

成功了。 VStack 导致了问题。