0

我有一个LazyVStack,有很多行。代码:

struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(0 ..< 100) { i in
                    Text("Item: \(i + 1)")
                        .onAppear {
                            print("Appeared:", i + 1)
                        }
                }
            }
        }
    }
}

最初在屏幕上只能看到大约 40 行,但onAppear触发了 77 行。这是为什么,为什么在屏幕上真正可见之前调用它?我不明白为什么 SwiftUI 必须“预加载”它们。

有没有办法解决这个问题,或者如果这是有意的,我怎样才能准确地知道最后一个可见项目(接受不同的行高)?

编辑

状态的文档LazyVStack

堆栈是“惰性的”,因为堆栈视图在需要将它们呈现在屏幕上之前不会创建项目。

所以这一定是一个错误,我想?

4

2 回答 2

0

根据文档中的文字,onAppear不应该是这样的:

堆栈是“惰性的”,因为堆栈视图在需要将它们呈现在屏幕上之前不会创建项目。

但是,如果您在使其正常工作时遇到问题,请参阅下面的解决方案。


虽然我不确定为什么onAppear会提前触发 rows,但我已经创建了一个解决方案。这会读取滚动视图边界的几何形状和要跟踪的单个视图,比较它们,并设置它是否可见。

在此示例中,isVisible当最后一项的上边缘在滚动视图的边界中可见时,该属性会发生变化。由于安全区域,这可能不会在屏幕上可见,但您可以根据需要进行更改。

代码:

struct ContentView: View {
    @State private var isVisible = false

    var body: some View {
        GeometryReader { geo in
            ScrollView {
                LazyVStack {
                    ForEach(0 ..< 100) { i in
                        Text("Item: \(i + 1)")
                            .background(tracker(index: i))
                    }
                }
            }
            .onPreferenceChange(TrackerKey.self) { edge in
                let isVisible = edge < geo.frame(in: .global).maxY

                if isVisible != self.isVisible {
                    self.isVisible = isVisible
                    print("Now visible:", isVisible ? "yes" : "no")
                }
            }
        }
    }

    @ViewBuilder private func tracker(index: Int) -> some View {
        if index == 99 {
            GeometryReader { geo in
                Color.clear.preference(
                    key: TrackerKey.self,
                    value: geo.frame(in: .global).minY
                )
            }
        }
    }
}
struct TrackerKey: PreferenceKey {
    static let defaultValue: CGFloat = .greatestFiniteMagnitude

    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }
}
于 2021-11-28T22:46:40.987 回答
0

在此处输入图像描述

它按照我上面的评论工作。

struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(0 ..< 100) { i in
                    Text("Item: \(i + 1)")
                        .id(i)
                        .frame(width: 100, height: 100)
                        .padding()
                        .onAppear { print("Appeared:", i + 1) }
                }
            }
        }
    }
}
于 2021-11-28T23:30:24.040 回答