在swift中有几种引用,一个通过@binding var param来引用原变量的值,在子函数或子view中修改 param,但我们也经常使用@environmentobject来引用全局数据。
例如:
struct testenvsubview: view {
@environmentobject var globaldata : globaldata
@binding var firstid:int
@binding var secondid:int
@state private var showeditdetail = false
var body: some view {
@state var mydata = globaldata[firstid][secondid]
vstack {
button(action: {
mydata.name = "test"
self.showeditdetail = true
})
{
text(mydata.name)
}
}
}
}
在这个例子中,我们通过定义
@environmentobject var greenhousedata : greenhousedata
来引用全局的数据greenhousedata,当然在上一层view中,应该有加载 .environmentobject(greenhousedata),不然在子view也加载不到数据。
如
struct testplant: identifiable {
let id = uuid()
var name: string
}
class globaldata: observableobject {
@published var globaldata: [[greenhouse]] = [[]]
init() {
let globaldata1 = [testplant(name: "plant 1"), testplant(name: "plant 2")]
let globaldata2 = [testplant(name: "plant 3"), testplant(name: "plant 4")]
globaldata = [globaldata1, globaldata2]
}
}
struct testenvparentview: view {
@stateobject var globaldata = globaldata()
@state private var firstid = 0
@state private var secondid = 0
@state private var showdetail = false
@environment(\.presentationmode) var presentationmode
var body: some view {
vstack() {
spacer()
button(action: {
self.showdetail = true
})
{text("click")}
}
spacer()
}
}
}.sheet(ispresented: $showdetail){
testenvsubview(firstid:$firstid,secondid:$secondid)
}
.environmentobject(globaldata)
}
}
但发现点击button后,text(mydata.name)没有变化,我在想不是@environmentobject来引用全局数据,怎么就没更改呢。
其实是因为:@state var mydata = globaldata[firstid][secondid]是一个复制变量,并不是原globaldata[firstid][secondid]的副本的引用,而修改mydata,是不会对mydata影响的。
第一步:我想是不是,可以直接操作globaldata[firstid][secondid],
修改下代码为:
var body: some view {
@state var mydata = globaldata.[firstid][secondid]
vstack {
button(action: {
self.globaldata.[firstid][secondid].name = "test"
self.showeditdetail = true
})
{
text(mydata.name)
}
}
}
点击button后,text(mydata.name)变化了,并且返回上一层view也变化了。
这直接操作globaldata[firstid][secondid]是成功的!!!
但是我感觉这么直接操作globaldata[firstid][secondid],太繁琐了,能不能用一个变量赋值,就简单点。
第二步:我就想用@state var mydata = globaldata.[firstid][secondid]修改为
var mydata = globaldata[firstid][secondid],但运行后,@state var mydata = globaldata.[firstid][secondid].name没有变化!这是什么鬼。
分析:
var mydata = globaldata[firstid][secondid], 应该是 globaldata[firstid][secondid]的一个复制品,
第三步:使用var mydata = $globaldata[firstid][secondid]来引用原数据,再直接wrappedvalue访问,并修改它的值。
var body: some view {
var mydata = $globaldata.[firstid][secondid]
vstack {
button(action: {
mydata.wrappedvalue.name = "test"
self.showeditdetail = true
})
{
text(mydata.wrappedvalue.name)
}
}
}
发现是成功的!
总结:为什么不同的方法访问globaldata[firstid][secondid],修改都不行呢, 这里总结下:
1、@state mydata = globaldata[firstid][secondid]
这个方式使用了@state
属性包装了mydata变量,以便你可以在视图中使用它,并在需要时更新它。当你使用这种方式时,mydata变量的值会在视图重绘时被重新初始化。因此,如果你想要在视图之间共享数据,你应该使用其他方法。
当你将数据源中的globaldata数组中的某个元素存储在@state
属性中时,它会创建一个mydata变量的本地副本,而不是引用原始数据源中的testplant实例。因此,对mydata变量的任何更改都不会影响原始数据源中的testplant实例。
2、let mydata = globaldata[firstid][secondid].wrappedvalue
这个方式使用了wrappedvalue
属性来获取绑定属性的实际值,并将其存储在myplant
变量中。当你使用这种方式时,mydata变量存储的是数据源中的testplant实例的一个副本,而不是一个绑定属性的引用。因此,如果你修改mydata变量的值,它不会更新到数据源中。
3、let mydata = $globaldata[firstid][secondid]
----唯一可以在视图中直接修改globaldata[firstid][secondid].原值的方法。
这个方式使用了$
符号来获取绑定属性的引用,以便你可以在视图中修改数据源中的属性或变量。当你使用这种方式时,mydata变量存储的是一个绑定属性的引用,你可以使用它来更新数据源中的属性或变量。但是,当你访问mydata变量的值时,它返回的是绑定属性的实际值,而不是绑定属性的引用,需要使用 mydata.wrappedvalue.value来修改原值:
mydata.wrappedvalue.name = "test"
4、let mydata = $globaldata[firstid][secondid].wrappedvalue
这个方式结合了前两种方式的特点,使用了$
符号来获取绑定属性的引用,并使用wrappedvalue
属性来获取绑定属性的实际值。当你使用这种方式时,mydata变量存储的是数据源中的testplant实例的一个副本,而不是一个绑定属性的引用。
最后:还有一个问题:
使用let mydata = $globaldata[firstid][secondid]时,为什么一定用mydata. wrappedvalue.name = newvalue 修改,而不是mydata.name = newvalue来修改?
原因是:
使用 mydata.wrappedvalue.name = newvalue
来修改 globaldata 对象的属性,而不是使用 mydata.name = newvalue
。这是因为 mydata 变量实际上是一个 binding
对象,而 name
属性是 testplant
对象的一个属性。如果你直接使用 mydata.name = newvalue
来修改属性,编译器会报错,因为 binding
对象没有 name
属性。
发表评论