SwiftUI_2_属性包装器
参考:
https://blog.csdn.net/weixin_45727359/article/details/109108544
https://zhuanlan.zhihu.com/p/141229504?from_voters_page=true
SwiftUI学习之@State、@Binding、@ObservedObject、@EnvironmentObject、@StateObject
@StateObject 和 @ObservedObject 的区别和使用
@ObservedObject, @State, and @EnvironmentObject的区别
ObservableObject研究——想说爱你不容易 ❤️
SwiftUI2.0 —— App、Scene、新的代码结构(二)

一、属性包装器(property wrapper):
@State、@Binding、@StateObject
@ObservedObject、
@EnvironmentObject
@State
在 SwiftUI 内部会被自动转换为一对 setter 和 getter,对这个属性进行赋值的操作将会触发 View 的刷新
@State修饰引用对象,修改值是无效的。
1、单一视图的本地状态:修改value,更新view的value
2、值传递,父视图value传递给子视图value1,修改value1,不影响value
3、修饰对象(引用类型),修改值(无效),view不更新
@StateObject
顾名思义,它是@State的引用类型版本。
对于@State,SwiftUI 1.0时代,如果想将引用类型作为source of truth,通常的方法是使用@EnvironmentObject或者 @ObservedObject。
@StateObject 和 @ObservedObject 的区别就是实例是否被创建其的View所持有,其生命周期是否完全可控。
由于@ObservedObject的机制问题,其创建的实例并不被当前View所拥有(当前View无法管理其生命周期),因此在一些特殊的情况下会出现不可预料的结果。
@binding
@ObservedObject
+ @binding ----- > @StateObject + @binding
它做的事情是将值语义的属性“转换”为引用语义。对被声明为 @Binding 的属性进行赋值,改变的将不是属性本身,而是它的引用,这个改变将被向外传递.
使用@binding时,注意$符号的使用(对一个由 @ 符号修饰的属性,在它前面使用 $ 所取得的值,被称为投影属性 (projection property))
@ObservedObject
从一个视图传递到另一个视图。
在ObservableObject研究——想说爱你不容易中,我们探讨过SwiftUI更倾向于我们不要创建一个沉重的Singel source of truth,而是将每个功能模块作为独立的状态机(一起组合成一个大的状态app),使用能够对生命周期和作用域更精确可控的手段创建区域性的source of truth。
@EnvironmentObject
比它们都更进一步:可以把一个对象注入环境,以便任何子视图都可以自动获得该对象的访问能力
@ObservedObject的另一个问题就是,view之间数据的共享是通过class作为引用数据类型的特点来传递。也就是说不同view之间,必须引用同一个class实例。那么这样就会造成一个问题,比如,有A B C D 四个view, A里边有个navigationLink指向B, B指向C,C指向D,如果仅仅A和D想通过@ObservedObject来共享数据的话,那么这个ObservedObject必须从A传递到B,然后到C到D。所以这种情况就需要用到@EnvironmentObject。
二、应用
@State
1、基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | struct User { var firstName = "Bilbo" var lastName = "Baggins" } struct ContentView : View { @ State private var user = User () //1 var body : some View { VStack { Text ( "Your name is \( user . firstName ) \( user . lastName )." ) //2 TextField ( "First name" , text : $ user . firstName ) //3 TextField ( "Last name" , text : $ user . lastName ) } } } |
2、值传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | struct ContentView : View { @ State var count = "eeer" var body : some View { VStack { MapView ( count1 : count ) } Text ( count ) . padding () } } struct MapView : View { @ State var count1 : String var body : some View { Button ( "点我" ) { //添加一个按钮,指定标题文字为 First button self . count1 = "1234567890" } } } |
@Binding
@Binding的作用是在保存状态的属性和更改数据的视图之间创建双向连接
将当前属性连接到存储在别处的单一数据源(single source of truth)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | struct Product : Identifiable { var isFavorited : Bool var title : String var id : String } struct FilterView : View { @ Binding var showFavorited : Bool //3 var body : some View { Toggle ( isOn : $ showFavorited ) { //4 Text ( "Change filter" ) } } } struct ProductsView : View { let products : [ Product ] = [ Product ( isFavorited : true , title : "ggggg" , id : "1" ), Product ( isFavorited : false , title : "3333" , id : "2" )] @ State private var showFavorited : Bool = false //1 var body : some View { List { FilterView ( showFavorited : $ showFavorited ) //2 ForEach ( products ) { product in if ! self . showFavorited || product . isFavorited { Text ( product . title ) } } } } } |
@StateObject 和 @ObservedObject
@StateObject 和 @ObservedObject 的区别就是实例是否被创建其的View所持有,其生命周期是否完全可控。
@ObservedObject创建的实例生命周期可能长于/短于/等于当前的View的生命周期
三段代码,三种结果,这也就是为什么苹果要新增@StateObject的原因——让开发者可以明确地了解并掌握实例的生命周期,消除不确定性!
苹果使用@StateObject一方面修复了之前的隐患,同时通过SwiftUI2.0众多新特性的引入,进一步完善了Data Flow的实现手段。
SwiftUI 2.0 —— @StateObject 研究
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class User : ObservableObject { @ Published var firstName = "Bilbo" @ Published var lastName = "Baggins" } struct ContentView : View { @ ObservedObject private var user = User () //@ObservedObject属性包装器只能用于符合ObservableObject协议的类型 var body : some View { VStack { Text ( "Your name is \( user . firstName ) \( user . lastName )." ) TextField ( "First name" , text : $ user . firstName ) TextField ( "Last name" , text : $ user . lastName ) } } } |
@EnvironmentObject
@EnvironmentObject
属性包装器是说明属性的数据是来自环境,而不是在本地创建的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | class User : ObservableObject { @ Published var name = "Taylor Swift" } struct EditView : View { @ EnvironmentObject var user : User var body : some View { TextField ( "Name" , text : $ user . name ) } } struct DisplayView : View { @ EnvironmentObject var user : User var body : some View { Text ( user . name ) } } struct ContentView : View { let user = User () var body : some View { VStack { EditView (). environmentObject ( user ) DisplayView (). environmentObject ( user ) } } } |
把 ContentView
改成下面这样:你会发现结果一样。我们现在是把 user
放到 ContentView
的环境中,但是因为 EditView
和 DisplayView
都是 ContentView
的子视图,所以它们自动继承了 ContentView
的环境。
1 2 3 4 5 | VStack { EditView () DisplayView () } . environmentObject ( user ) |
· 分享4款.NET开源、免费、实用的商城系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了