test
视图具有@State showTitle
,title
和items
,其中title
值文本由分配给CTA {{ 1}}。
show title
状态更改时,showTitle
视图的正文内容中显示的值也相应更改:
test
尽管Text({ self.showTitle ? "Yes, showTitle!" : "No, showTitle!" }())
是数组closure
中的值的情况不变。为什么闭包不触发标题状态?
items
我已经用Foobar作为NestedView(title: $0.title())
和Struct
进行了测试。
Class
可以预期的是,“案例2”具有与“案例1”中类似的副作用,应该在import SwiftUI
struct Foobar: Identifiable {
var id: UUID = UUID()
var title: () -> String
init (title: @escaping () -> String) {
self.title = title
}
}
struct test: View {
@State var showTitle: Bool = true
@State var title: String
@State var items: [Foobar]
var body: some View {
VStack {
Group {
Text("Case 1")
Text({ self.showTitle ? "Yes, showTitle!" : "No, showTitle!" }())
}
Group {
Text("Case 2")
ForEach (self.items, id: \.id) {
NestedView(title: $0.title())
}
}
Button("show title") {
print("show title cb")
self.showTitle.toggle()
}
}.onAppear {
let data = ["hello", "world", "test"]
for title in data {
self.items.append(Foobar(title: { self.showTitle ? title : "n/a" }))
}
}
}
}
struct NestedView: View {
var title: String
var body: some View {
Text("\(title)")
}
}
切换上显示“ n / a”。
输出:
答案 0 :(得分:1)
据我了解,初始代码不起作用的原因与传递给Array并保存值副本的showTitle属性有关
您应该责怪闭包在onAppear
时获取值。基本上由于这个原因,SwiftUI不会在showTitle
值更改时刷新列表,因为SwiftUI可以使用Binding
来知道何时重新呈现列表。
我可以提供两种替代解决方案,它们不需要另一个类来保存bool值。两种解决方案都涉及与SwiftUI进行通信,您需要showTitle
绑定才能刷新标题。
title
使用闭包,将标题计算推迟到列表生成器:struct Foobar: Identifiable {
var id: UUID = UUID()
var title: String
init (title: String) {
self.title = title
}
}
...
ForEach (self.items, id: \.id) {
NestedView(title: self.showTitle ? $0.title : "n/a" )
}
...
.onAppear {
let data = ["hello", "world", "test"]
self.items = data.map { Foobar(title: $0) }
}
(Binding<Bool>) -> String
,从视图中注入$showTitle
绑定:struct Foobar: Identifiable {
var id: UUID = UUID()
var title: ((Binding<Bool>) -> String)
init (title: @escaping (Binding<Bool>) -> String) {
self.title = title
}
}
...
ForEach (self.items, id: \.id) {
// here we pass the $showTitle binding, thus SwiftUI knows to re-render
// the view when the binding value is updated
NestedView(title: $0.title(self.$showTitle))
}
...
.onAppear {
let data = ["hello", "world", "test"]
self.items = data.map { Foobar(title: { $0.wrappedValue ? title : "n/a" })) }
}
我个人会采用第一个解决方案,因为它可以更好地传达意图。
答案 1 :(得分:0)
据我了解,初始代码不起作用的原因与传递给Array并保存showTitle
副本的value
属性有关(创建了一个唯一的副本数据)。
我确实认为@State将使其可控和可变,而closure
将捕获并存储引用(创建共享实例)。换句话说,要拥有reference
而不是复制的value
!如果不是这样,请随时纠正我,但是根据我的分析,这就是我的样子。
话虽如此,我仍然保留了最初的思考过程,我仍然想将closure
传递给Array,并传播状态变化,从而导致副作用,相应于对其的引用!
因此,我使用了相同的模式,但没有依赖showTitle
Bool
的原始类型,而是创建了一个符合协议Class
的{{1}}:因为ObservableObject
是Classes
。
所以,让我们看一下它是如何工作的:
reference types
预期结果: