【问题标题】:How can I prevent SwiftUI to Re-Initializing Initialized-View in a ForEach loop?如何防止 SwiftUI 在 ForEach 循环中重新初始化 Initialized-View?
【发布时间】:2021-02-08 08:10:26
【问题描述】:

我有一个按钮,可以将 CircleInfo 类型的新对象添加到一个名为 circleArray 的数组中!这个数组将用于为 ForEach 循环提供数据,除了我注意到的一个大问题外,所有的都在工作!

问题:当我将新对象添加到数组时,更新数组的动作也使 ForEach 更新自身,它使应用程序和内存以比简单的 Circle,因为它再次开始 Re-Initializing Initialized-View!例如我们在屏幕中有 10 个圆,只要我添加新的圆,Foreach 就会渲染所有渲染的 10 个圆加上新的圆! 也许你说这是 SwiftUI 的工作方式,它以微小的变化来绘制屏幕,​​但我告诉你,随着越来越多的数据它会吃掉这么多内存和 CPU!

我的目标:我想找到一种将新对象添加到屏幕的新方法,更有可能是过度添加或类似那些不需要 ForEach 的东西!因为只要我们改变数组的项或数组的范围,它就会再次绘制!

struct CircleInfo: Identifiable {

    let id: UUID = UUID()
    let circleColor: Color = Color(red: Double.random(in: 0...1), green: Double.random(in: 0...1), blue: Double.random(in: 0...1))

}

    struct CircleView: View {

    let circleInfo: CircleInfo
    let index: Int

    init(circleInfo: CircleInfo, index: Int) {

        self.circleInfo = circleInfo
        self.index = index
        
        print("initializing circle:", index)
    }

    var body: some View {

        Circle()
            .fill(circleInfo.circleColor)
            .frame(width: 200, height: 200, alignment: .center)
        
    }
    
}

    struct ContentView: View {

    @State var circleArray: [CircleInfo] = [CircleInfo]()

    var body: some View {

        GeometryReader { geometry in

            Color
                .yellow
                .ignoresSafeArea()

            ForEach(Array(circleArray.enumerated()), id:\.element.id) { (index, item) in

                CircleView(circleInfo: item, index: index)
                    .position(x: geometry.size.width/2, y: 20*CGFloat(index) + 100)
                
            }
 
        }
        .overlay(button, alignment: Alignment.bottom)
  
    }

    var button: some View {

        Button("add Circle") { circleArray.append(CircleInfo()) }
  
    }
 
}

【问题讨论】:

    标签: swiftui


    【解决方案1】:

    实现目标的最简单方法是使用 CircleView 数组而不是 CircleInfo

    首先使CircleView可识别:

    struct CircleView: View, Identifiable {
    
        let id: UUID = UUID()
    …
    
    

    然后在ForEach中使用CircleView数组

    
    struct ContentView: View {
    
        @State var circleArray: [CircleView] = [CircleView]()
    
        var body: some View {
    
            GeometryReader { geometry in
    
                Color
                    .yellow
                    .ignoresSafeArea()
    
                    ForEach(circleArray) { item in
                        item
                            .position(x: geometry.size.width/2, y: 20*CGFloat(item.index) + 100)
    
                }
    
            }
            .overlay(button, alignment: Alignment.bottom)
    
        }
    
        var button: some View {
    
            Button("add Circle") { circleArray.append(CircleView(circleInfo: CircleInfo(), index: circleArray.count)) }
    
        }
    
    }
    

    你肯定会少用 CPU。

    【讨论】:

    • 我喜欢你的方式,你以前遇到过这个问题还是你找到了答案?
    • @swiftPunk 以前从未遇到过这个问题,但自从它问世以来,我已经编写了很多 SwiftUI 代码,所以每次CircleView 时不调用初始化程序似乎是相对自然的。已添加。
    • 我们都在向数组中添加新项目,我正在向数组中添加 CircleInfo,但您正在添加 CircleView!为什么我们得到不同的结果?我的意思是,我们都为 ForEach 更新了一个数组,为什么要这么大的尊重?
    • ForEach 得到一个闭包作为尾随参数。当@State 变量被更改时,它会导致SwiftUI 重绘ContentView,这反过来又使ForEach 循环通过circleArray 并使用circleArray 的每个项目执行闭包。在您的版本中,闭包实例化了一个新的CircleView,在我的版本中,该角色被委派给按钮,CircleView 实例缓存在circleArray
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    • 1970-01-01
    相关资源
    最近更新 更多