您现在的位置是:首页 >技术教程 >SwiftUI中EnvironmentObject使用中,直接修改数据源的原值的方法网站首页技术教程

SwiftUI中EnvironmentObject使用中,直接修改数据源的原值的方法

小老板老工程师 2024-07-01 11:59:45
简介SwiftUI中EnvironmentObject使用中,直接修改数据源的原值的方法

在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 属性

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。