简体   繁体   English

LazyVStack 当一个视图发生变化时初始化所有视图 SwiftUI

[英]LazyVStack Initializes all views when one changes SwiftUI

I have a LazyVStack which I would like to only update one view and not have all others on screen reload.我有一个LazyVStack ,我只想更新一个视图,而不是在屏幕上重新加载所有其他视图。 With more complex cells this causes a big performance hit.对于更复杂的单元,这会对性能造成很大影响。 I have included sample code我已经包含了示例代码

import SwiftUI

struct ContentView: View {
    @State var items = [String]()
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(self.items, id: \.self) { item in
                    Button {
                        if let index = self.items.firstIndex(where: {$0 == item}) {
                            self.items[index] = "changed \(index)"
                        }
                    } label: {
                        cell(text: item)
                    }
                }
            }
        }
        .onAppear {
            for _ in 0...200 {
                self.items.append(NSUUID().uuidString)
            }
        }
    }
}
struct cell: View {
    let text: String
    init(text: String) {
        self.text = text
        print("init cell", text)
    }
    var body: some View {
        Text(text)
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

As you can see even when only changing 1 cell the init gets called for every cell.正如您所看到的,即使仅更改 1 个单元格,也会为每个单元格调用 init。 Is there anyway to avoid this?有没有办法避免这种情况?

Here is a working code, there is some points to mention, View in SwiftUI would get initialized here and there or anytime SwiftUI thinks it needed.这是一个工作代码,有几点需要提一下,SwiftUI 中的视图会在这里和那里或 SwiftUI 认为需要的任何时间进行初始化。 But the body of View would get computed if really some value in body changed, It is planed to work like that.但是,如果 body 中的某些值确实发生了变化,则 View 的 body 将被计算出来,它计划像那样工作。 there is some exceptions as well, Like body get computed even the values that used in the body were as before with no change, I do not want inter to that topic, But in your example and in your issue, we want SwiftUI renders only the changed View, for this goal the down code works well without issue, but as you can see I used VStack, if we change VStack to LazyVStack , SwiftUI would renders some extra view due its undercover codes, and if you scroll to down and then to up, it would forget all rendered view and data in memory and it will try to render the old rendered views, so it is the nature of LazyVStack , we cannot do much about it.也有一些例外,就像 body 得到计算一样,即使在 body 中使用的值没有改变,我不想进入那个主题,但是在你的例子和你的问题中,我们希望 SwiftUI 只呈现更改视图,为此目标,向下代码运行良好,没有问题,但正如您所见,我使用了 VStack,如果我们将VStack更改为LazyVStack ,SwiftUI 将呈现一些额外的视图,因为它的隐藏代码,如果你向下滚动然后到up,它会忘记 memory 中的所有渲染视图和数据,它会尝试渲染旧的渲染视图,所以这是LazyVStack的本质,我们对此无能为力。 Apple want LazyVStack be Lazy.苹果希望LazyVStack是懒惰的。 But you can see that LazyVStack would not render all views, but some of them that needs to works.但是您可以看到LazyVStack不会渲染所有视图,但其中一些需要工作。 we cannot say or know how much views get rendered in Lazy way, but for sure not all of them.我们不能说或知道有多少视图以 Lazy 方式呈现,但肯定不是全部。

let initializingArray: () -> [String] = {
    var items: [String] = [String]()

    for _ in 0...200 { items.append(UUID().uuidString) }

    return items
}


struct ContentView: View {

    @State var items: [String] = initializingArray()
    
    var body: some View {
        ScrollView {
            VStack {
                ForEach(items, id: \.self) { item in

                    Button(action: {
                        
                        if let index = self.items.firstIndex(where: { $0 == item }) {
                            items[index] = "changed \(index)"
                        }
                        
                    }, label: {
                        ItemView(item: item)
                    })
                    
                }
            }
        }

    }
}

struct ItemView: View {
    
    let item: String
    
    var body: some View {
        print("Rendering row for:", item)
        return Text(item)
        
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM