首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >LazyVStack - row onAppear称为早期。

LazyVStack - row onAppear称为早期。
EN

Stack Overflow用户
提问于 2021-11-28 21:59:13
回答 3查看 804关注 0票数 1

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

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

最初屏幕上只有大约40行可见,而77行则触发了onAppear。为什么是这个,为什么它在屏幕上可见之前就被调用了?我不明白为什么SwiftUI必须“预加载”它们。

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

编辑

文档 for LazyVStack声明:

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

那这一定是个虫子吧,我猜?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-11-28 22:46:40

文档的话来说,onAppear不该是这样的:

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

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

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

在本例中,当上一项的顶部边缘在滚动视图的边界中可见时,isVisible属性将发生更改。这可能不是当它在屏幕上可见时,由于安全区域,但您可以将此更改为您的需要。

代码:

代码语言:javascript
复制
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
                )
            }
        }
    }
}
代码语言:javascript
复制
struct TrackerKey: PreferenceKey {
    static let defaultValue: CGFloat = .greatestFiniteMagnitude

    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }
}
票数 0
EN

Stack Overflow用户

发布于 2021-11-28 23:30:24

根据我上面的评论,这是可行的。

代码语言:javascript
复制
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) }
                }
            }
        }
    }
}
票数 0
EN

Stack Overflow用户

发布于 2022-11-14 14:07:00

这似乎令人难以置信,但是仅仅添加一个包含GeometryReader的ScrollView就可以解决这个问题。

代码语言:javascript
复制
GeometryReader { _ in
        ScrollView(.vertical, showsIndicators: false) {
            LazyVStack(spacing: 14) {
                Text("Items")
                LazyVStack(spacing: 16) {
                    ForEach(viewModel.data, id: \.id) { data in
                        MediaRowView(data: data)
                        .onAppear {
                            print(data.title, "item appeared")
                        }
                    }
                    if viewModel.state == .loading {
                        ProgressView()
                    }
                }
            }
            .padding(.horizontal, 16)
        }
    }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70147827

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档