gpt4 book ai didi

swift - ForEach 不必要地更新 View

转载 作者:行者123 更新时间:2023-12-04 09:19:11 25 4
gpt4 key购买 nike

这是我的代码:

struct TestPageView: View {
@State var blocks: [String] = ["1", "2", "3"]

var body: some View {
VStack {
ForEach(blocks, id: \.self) { block in
TestView(block, $blocks)
}
}
}
}

struct TestView: View {
@Binding var blocks: [String]
let block: String

init(_ block: String, _ blocks: Binding<[String]>){
print("Created empty view")
self.block = block
self._blocks = blocks
}
var body: some View {
TextField("Test view", text: .constant(block), onCommit: {
print("Committed")
blocks.append("new element")
})
}
}
基本思想是我的 ForEach 循环遍历我的 block ,显示它们。当用户在一个 block 被聚焦时执行一个 Action ( onCommit 这里),我需要一个新的 block 添加到列表中。这段代码实际上是正确的;问题在于性能。使用上述打印语句,输出如下:
Created empty view
Created empty view
Created empty view
Committed
Created empty view
Created empty view
Created empty view
Created empty view
所以看起来 ForEach 重新渲染了列表中的所有项目。在这个简单的例子中,这不是问题,但在我真正的问题中,重新创建所有 TestView s 是一项大任务,完全没有必要:我只需要添加新元素,其他一切都可以保持不变。有没有好的方法来做到这一点?
备注 : \.self不是问题。我已经尝试过使“ block ”符合 Identifiable . ForEach 仍然无法识别相同的 id 并且仍然再次创建 View 。

最佳答案

SwiftUI 会发生很多黑盒行为,遗憾的是没有足够的文档。
在高层次上,SwiftUI 会尝试查看哪些 View 的主体需要失效和重新计算。主体重新渲染是一项相当昂贵的操作,因此 SwiftUI 尝试仅在需要时执行此操作。
话虽如此,init 应该很便宜! 在 SwiftUI 构建和区分 View 树时, View 一直被初始化。
您应该查看的是计算主体的次数,而不是 init 的次数。叫做。
碰巧的是,TestView的正文 每次都重新计算。为什么?其原因几乎可以肯定是由于 @Binding var blocks .
当任何@State@Binding@ObservedObject更改后,SwiftUI 会重新计算正文。因此,拥有 @Binding var blocks , 告诉 SwiftUI TestView取决于那个状态,当blocks数组被更新,它使所有 TestView 无效s 在 ForEach 中。
你可以做的是传递 onCommit关闭至TestView并将新元素添加到 blocks由家长 TestPageView ,这也是 blocks 的所有者:

struct TestView: View {
var block: String
var onCommit: ((String) -> Void)? = nil

var body: some View {
TextField("Test view", text: .constant(self.block), onCommit: {
self.onCommit?(self.block)
})
}
}
// in TestPageView
ForEach(blocks, id: \.self) { block in
TestView(block: block) { txt in
self.blocks.append(txt)
}
}
不幸的是,这(令我惊讶的是)也不能解决这种情况,因为出于某种原因 SwiftUI 确定具有闭包属性意味着应该重新计算 View 。
幸运的是,有一种方法可以最终让 SwiftUI 相信我们知道 View 实际上不需要重新计算以符合 Equatable。 ,在这里我们可以准确地告诉 SwiftUI 如何比较 View 更改。
extension TestView: Equatable {
static func == (lhs: TestView, rhs: TestView) -> Bool {
lhs.block == rhs.block
}
}
并使用 .equatable使用 View 时的修饰符:
TestView(block: block) { txt in
self.blocks.append(txt)
}.equatable()

仅供引用,使用 .constant(value)绑定(bind)没有多大意义,除了预览。我把它留在这里,因为你在你的例子中使用了它。

关于swift - ForEach 不必要地更新 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63125224/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com