我如何确定视图在 SwiftUI 中的最后一行代码之前完全渲染?

2024-05-01

我有一些代码,它们必须在视图完全渲染和绘制后被触发,因为我们知道视图的 onAppear 或 onChange ,它们对于了解视图是否已出现或正在更改很有用,但它们是确保视图已 100% 渲染没有用,例如,如果我们在视图中获得 ForEach、List 或 Form,您可以看到视图,但由于层次结构,ForEach 仍然可以在视图上工作,因此这意味着视图确实未完全加载,如果您提供动画修改器,那么您也可以看到它,它可能会变得更加明显,所以我的目标是知道如何 100% 确定我的视图会渲染其中的所有内容,并且不再需要运行代码来渲染视图?

感谢您的阅读和帮助


Example:

import SwiftUI


struct ContentView: View {

    var body: some View {

        HierarchyView()
            .onAppear() { print("HierarchyView"); print("- - - - - - - - - - -") }

    }

}



struct HierarchyView: View {

    var body: some View {

        ZStack {

            Color
                .blue
                .ignoresSafeArea()
                .onAppear() { print("blue"); print("- - - - - - - - - - -")  }

            VStack {
                
                Text("Top text")
                    .onAppear() { print("Top Text") }
                
                Spacer()
                    .onAppear() { print("Top Spacer") }
                
            }
            .onAppear() { print("Top VStack"); print("- - - - - - - - - - -")  }

            VStack {

                VStack {

                    VStack {

                        ForEach(0..<3, id:\..self) { index in
                            
                            Text(index.description)
                                .onAppear() { print(index.description); if (index == 0) { print("Last of second ForEach!!!") } }
                            
                        }
  
                    }
                    .onAppear() { print("Middle VStack Inside DEEP"); print("- - - - - - - - - - -")  }

                }
                .onAppear() { print("Middle VStack Inside"); print("- - - - - - - - - - -")  }

            }
            .onAppear() { print("Middle VStack Outside"); print("- - - - - - - - - - -")  }
            
            
            
            VStack {
                
                Spacer()
                    .onAppear() { print("Bottom Spacer") }
                
                
                ForEach(0..<3, id:\..self) { index in
                    
                    Text(index.description)
                        .onAppear() { print(index.description); if (index == 0) { print("Last of first ForEach!!!") } }
                    
                }

                
                
                
                Text("Bottom text")
                    .onAppear() { print("Bottom Text") }
                
            }
            .onAppear() { print("Bottom VStack"); print("- - - - - - - - - - -")  }
            
            
        }
        .onAppear() { print("ZStack of HierarchyView"); print("- - - - - - - - - - -") }
        
        
    }
 
}

AFAIK,没有通用的方法来判断所有可能的后代视图何时“出现”(即会解雇他们的onAppear).

根据您的想法,“出现”与“呈现”不同。一般来说,每个视图决定何时渲染其子视图。例如,LazyVStack只会在需要渲染时创建其元素。自定义视图符合UIViewControllerRepresentable可以决定做任何它想做的事。

考虑到这一点(假设渲染==出现),您可以采取的方法是“跟踪”您关心的那些“出现”的视图,并在它们全部出现时触发回调。

您可以创建一个视图修改器来跟踪每个视图的“渲染”状态,并使用PreferenceKey收集所有这些数据:

struct RenderedPreferenceKey: PreferenceKey {
    static var defaultValue: Int = 0
    static func reduce(value: inout Int, nextValue: () -> Int) {
        value = value + nextValue() // sum all those remain to-be-rendered
    }
}

struct MarkRender: ViewModifier {
    @State private var toBeRendered = 1
    func body(content: Content) -> some View {
        content
            .preference(key: RenderedPreferenceKey.self, value: toBeRendered)
            .onAppear { toBeRendered = 0 }
    }
}

然后,创建便捷方法View简化其用法:

extension View {
    func trackRendering() -> some View {
        self.modifier(MarkRender())
    }

    func onRendered(_ perform: @escaping () -> Void) -> some View {
        self.onPreferenceChange(RenderedPreferenceKey.self) { toBeRendered in
           // invoke the callback only when all tracked have been set to 0,
           // which happens when all of their .onAppear are called
           if toBeRendered == 0 { perform() }
        }
    }
}

用法是:

VStack {
    ForEach(0..<3, id:\..self) { index in
        Text("\(index)").trackRendering()
    }
}
.onRendered {
    print("Rendered")
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我如何确定视图在 SwiftUI 中的最后一行代码之前完全渲染? 的相关文章

随机推荐