swiftui - SwiftUI 视图会更新,即使它不依赖于动态属性
问题描述
我创建了一个ObservableObject
收集数据的视图和一个依赖于该对象的视图。更具体地说,UI 的某些部分依赖于它的一个属性,而其他部分依赖于其他属性。
工作方式ObservableObject
是,如果它的任何Published
属性被更新(如果它没有改变,则事件)它会发送一个objectWillChange
通知,触发订阅视图的更新。
但是,我确实希望只有依赖于ObservableObject
实际更改的属性的视图部分会被更新,而不是整个视图,因为主体计算很昂贵。
不幸的是,这不是我在实验中观察到的行为。例如,在下面的代码中,List
视图仅取决于“首选项”的color
动态属性,并且仅更新观察对象的属性。我观察到,当我键入文本时,列表及其行每次都会更新,即使它不依赖于.ObservedObject
TextField
text
debugPrint
text
struct ContentView: View {
@State private var list = ["foo", "bar", "baz"]
@StateObject private var preferences = Preferences()
var body: some View {
VStack {
List(list, id: \.self) { element in
Text(element)
.foregroundColor(preferences.color)
.debugPrint("Row view updated")
}
Spacer()
HStack {
TextField("Text", text: $preferences.text)
.debugPrint("Text field view updated")
Button("Toogle Color", action: toggleColor)
.debugPrint("Button view updated")
}
}
.padding()
.debugPrint("Content view updated")
}
func toggleColor() {
preferences.color = [.blue, .green, .orange, .red].randomElement()!
}
}
final class Preferences: ObservableObject {
@Published var color: Color = .blue
@Published var text: String = ""
}
extension View {
func debugPrint(_ elements: Any...) -> Self {
#if DEBUG
print(elements)
return self
#else
return self
#endif
}
}
这是正确和预期的行为吗?如何获得我上面描述的最佳行为,即body
在ObservedObject
更改时不调用,而只调用依赖于特定属性的组件?
我在较大的 SwiftUI 项目中观察到,这种行为会大大降低应用程序的速度并且不容易调试,因为唯一状态通常通过在根视图中注入的重ObservedObject
s (或StateObject
)来强制执行,并在许多地方使用。我观察到了这种行为,即使是带有大量 UI 布局的小组件列表。
注意 1:我被定义所困扰,该StateObject
定义表明只有依赖于这些属性的视图才会更新(而不是在StateObject
更改时)。
SwiftUI 只为声明对象的结构的每个实例创建一次对象的新实例。当可观察对象的已发布属性发生更改时,SwiftUI 会更新依赖于这些属性的任何视图的部分 [...]
注2:如果我List
在自己的组件中定义,问题完全消失,渲染速度很快:
struct ListView: View {
let list: [String] // Also works with a @Binding
let color: Color // Also works if it would be `preferences`
var body: some View {
List {
ForEach(list, id: \.self) { element in
Row(text: element, color: color)
}
}
}
}
为什么在这种情况下视图更新很快?
解决方案
推荐阅读
- blazegraph - 如何在 Blazegraph 中插入带有 POST 请求正文的 SPARQL 数据?
- python - 在 Django 中处理不同但相似的模型层次结构的最佳方法是什么?
- java - 从已部署的 jar 访问 web-inf
- angular - 从 TypeScript 设置输入元素的值
- swift - 核心数据。按日期过滤
- php - 从 Laravel 中的更多表中检索数据的最佳方法
- node.js - Node.js:新创建的标头(res.header)未出现在 Chrome 中
- java - 如何在语法或解析错误上抛出有意义的错误
- android - 蓝牙 LE 扫描结果返回 null,但我知道我的外围设备正在广播心率服务 UUID(由第 3 方 BLE 应用程序验证)。为什么?
- php - 如何在 PHP 中随机选择和删除数组中的名称?