Swift 为什么@State数组属性中的闭包不会触发状态更改?
测试视图具有@State showTitle、title和项目,其中标题值文本由分配给CTA show title的闭包控制 当showTitle状态更改时,测试视图主体内容中显示的值也会相应更改:Swift 为什么@State数组属性中的闭包不会触发状态更改?,swift,swiftui,Swift,Swiftui,测试视图具有@State showTitle、title和项目,其中标题值文本由分配给CTA show title的闭包控制 当showTitle状态更改时,测试视图主体内容中显示的值也会相应更改: Text({ self.showTitle ? "Yes, showTitle!" : "No, showTitle!" }()) 而闭包是数组项中的值的情况不变。为什么闭包没有触发标题状态 NestedView(title: $0.title()) 我已经用Foobar作为Struct和Cla
Text({ self.showTitle ? "Yes, showTitle!" : "No, showTitle!" }())
而闭包是数组项中的值的情况不变。为什么闭包没有触发标题状态
NestedView(title: $0.title())
我已经用Foobar作为Struct和Class进行了测试
我们期望的是,案例2会产生与案例1类似的副作用,在showTitle切换上显示n/a
输出:
据我所知,初始代码不起作用的原因与传递给数组并保存值副本的showTitle属性有关,它创建了数据的唯一副本 我确实认为@State会使它变得可控和可变,闭包会捕获并存储引用并创建一个共享实例。换句话说,有一个引用,而不是复制的值!如果不是这样的话,请随时纠正我,但根据我的分析,情况就是这样 话虽如此,我保留了最初的思考过程,我仍然希望向数组传递一个闭包,并传播状态更改,从而对它的任何引用产生副作用 因此,我使用了相同的模式,但不是依赖于showttitle Bool的基元类型,而是创建了一个符合协议observeObject的类:因为类是引用类型 那么,让我们来看看这是如何实现的:
import SwiftUI
class MyOption: ObservableObject {
@Published var option: Bool = false
}
struct Foobar: Identifiable {
var id: UUID = UUID()
var title: () -> String
init (title: @escaping () -> String) {
self.title = title
}
}
struct test: View {
@EnvironmentObject var showTitle: MyOption
@State var title: String
@State var items: [Foobar]
var body: some View {
VStack {
Group {
Text("Case 1")
Text(self.showTitle.option ? "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.option.toggle()
print("self.showTitle.option: ", self.showTitle.option)
}
}.onAppear {
let data = ["hello", "world", "test"]
for title in data {
self.items.append(Foobar(title: { self.showTitle.option ? title : "n/a" }))
}
}
}
}
struct NestedView: View {
var title: String
var body: some View {
Text("\(title)")
}
}
结果如预期:
据我所知,初始代码不起作用的原因与传递给数组并保存值副本的showTitle属性有关 你是对的,你责怪关闭在出现的时候捕获了价值。基本上正因为如此,当showTitle值更改时,SwiftUI不知道如何刷新列表,因为SwiftUI不需要使用绑定来知道何时重新呈现列表 我可以提供两种可选的解决方案,不需要另一个类来保存bool值。这两种解决方案都涉及到与SwiftUI的通信,即您需要showTitle绑定来刷新标题 不要对标题使用闭包,请将标题计算推迟到列表生成器: 结构Foobar:可识别{ 变量id:UUID=UUID 变量标题:字符串 初始化标题:字符串{ self.title=标题 } } ... ForEach self.items,id:\.id{ NestedViewtitle:self.showTitle?$0.title:n/a } ... 奥纳佩尔先生{ 让数据=[你好,世界,测试] self.items=data.map{Foobartitle:$0} } 将标题闭包转换为绑定->字符串闭包,从视图中插入$showTitle绑定: 结构Foobar:可识别{ 变量id:UUID=UUID 变量标题:绑定->字符串 初始化标题:@转义绑定->字符串{ self.title=标题 } } ... ForEach self.items,id:\.id{ //这里我们传递$showthile绑定,因此SwiftUI知道如何重新渲染 //更新绑定值时的视图 NestedViewtitle:$0.titleself.$showTitle } ... 奥纳佩尔先生{ 让数据=[你好,世界,测试] self.items=data.map{foobartile:{$0.wrappedValue?title:n/a} }
就我个人而言,我会选择第一种解决方案,因为它能更好地传达意图。我看不出你认为你在哪里改变一个@State变量,它是一个闭包。你能更明确地说明你认为哪一行会这样做,以及你期望发生什么吗?嗨@matt,是的,这是NestedViewtitle:$0.title,标题调用,self.items.appendFoobartitle:{self.showtTitle?标题:n/a}我不明白。您是否认为更改您的州标题会在某种程度上左右摇摆,并更改Foobars中已经存在的标题?不是,而是在showTitle.toggle中,看到案例1中类似的副作用;这是为了将案例2OK的标题文本更改为n/a,这很清楚。好吧,所以你认为改变你的州剧名会在某种程度上改变现有的Foobar。那是不会发生的;Foobar的选择已经做出。如果你想改变你拥有的Foobar,你会说某事=self.showTitle?Foobartile:标题:Foobartile:不适用。
import SwiftUI
class MyOption: ObservableObject {
@Published var option: Bool = false
}
struct Foobar: Identifiable {
var id: UUID = UUID()
var title: () -> String
init (title: @escaping () -> String) {
self.title = title
}
}
struct test: View {
@EnvironmentObject var showTitle: MyOption
@State var title: String
@State var items: [Foobar]
var body: some View {
VStack {
Group {
Text("Case 1")
Text(self.showTitle.option ? "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.option.toggle()
print("self.showTitle.option: ", self.showTitle.option)
}
}.onAppear {
let data = ["hello", "world", "test"]
for title in data {
self.items.append(Foobar(title: { self.showTitle.option ? title : "n/a" }))
}
}
}
}
struct NestedView: View {
var title: String
var body: some View {
Text("\(title)")
}
}