SwiftUI 常用属性包装器

@State 可监听的基本类型

可监听的的数据属性,可以触发对应view的刷新。 用于基本数据类型,比如 Bool, Int, String

样例代码:

//声明 可监听的属性
@State private var showScore = true
Button(“ShowOrHide”) {
        //修改该属性,会触发执行 使用该属性的代码
        showScore = !showScore
}
if showScore {
       ScorePlate(model: model).padding(.top, 20)
}

@Binding 双向绑定两个属性

将一个 @State 属性绑定到一个 @Binding 属性上。 当双方发生改变时,都会改变另一个。

struct CategoryHome: View {
    @State var showingProfile = false
    var body: some View {
    List {
		//将 self.showingProfile 设为 true 来弹出 ProfileHost
                Button("Change") {
                    self.showingProfile = true
                }
            }
            .sheet(isPresented: $showingProfile) {
		//将 showingProfile 绑定到 ProfileHost的 shouldHide 上,然后 ProfileHost就可以控制shouldHide来做dismiss操作了。
                ProfileHost(shouldHide: $showingProfile)
            }
    }
}

struct ProfileHost: View {
    @Binding var shouldHide : Bool
    var body: some View {
	Button("Close") {
			// 绑定的showingProfile 也会改为false,将当前view 给dismiss掉
                        self.shouldHide = false
                    }
    }
}

Binding 属性可以用常量来初始化 比如上面的 Binding

 Binding.constant(false) 

Binding属性的初始化可以是

@Binding(projectedValue: .constant(1)) var currentPage : Int
    
    init(currentPage : Binding<Int>) {
        self._currentPage = currentPage
    }

@ObservedObject 引用可监听的对象类型

可监听指定类的所有属性变化情况 以刷新UI. 当@Published 属性变化时,存储该对象的变量声明为 @ObservedObject 时,使用到该实例对象代码会执行。 该属性可以从外部传入,或是View 对象自己创建。

样例代码:

//类必须实现 ObservableObject 协议
class ObserverbleModel: ObservableObject {
	//需要监听的属性 声明为 @Published。只有在Published 属性变化时,才会触发声明了 @ObservedObject 属性的View 的body 重绘
    @Published var score: Int = 0
    var bigScore : Int = 0
}

struct ScoreText: View {
    //需要监听的数据对象,声明为 @ObservedObject
    @ObservedObject var model: ObserverbleModel
	var body: some View {
        if model.bigScore > 5 {
            Text("Big good")
        }
        else {
            Text("Big bad")
        }
    }
}

@StateObject 初始化并持有的可监听对象

跟ObservedObject 一样都是可监听的。 不同的是,声明StateObject的View,必须是它创建的该对象(StateObject属性是只读的),该对象生命周期跟随View

@EnvironmentObject 无需手动传递的可监听对象

使用ObservedObject 时,需要将对象逐个传递给子View的属性。 如果使用环境对象EnvironmentObject,则只用传递一次,该View 和它下面的子View里,声明为 EnvironmentObject的对象都能被赋值。 一个View的EnvironmentObject 只能声明一个,声明多个的话,都会指向同一个对象。 EnvironmentObject只能通过 environmentObject 方法从view 外部传入。

样例代码:

//第一级子view
struct SubEnvironmentView : View {
    //声明了两个,但都会指向同一个对象
    @EnvironmentObject fileprivate var data : TestBigData
    @EnvironmentObject fileprivate var data2 : TestBigData
    var body: some View {
        VStack {
            Text("SubEnvironmentView").font(.largeTitle).foregroundColor(.red)
            if data.showText {
                Text(data2.text).foregroundColor(.red)
            }
            SubEnvironmentView2()
        }
    }
}
//第二级子view
struct SubEnvironmentView2 : View {
    @EnvironmentObject fileprivate var data : TestBigData
    var body: some View {
        VStack {
            Text("SubEnvironmentView2").font(.largeTitle).foregroundColor(.blue)
            if data.showText {
                Text(data.text).foregroundColor(.blue)
            }
        }
    }
}
//顶级view
struct StateObservedEnvironment: View {
    @StateObject fileprivate var data : TestBigData = TestBigData()
    @EnvironmentObject fileprivate var envData : TestBigData
    var body: some View {
        VStack {
            Toggle(isOn: $data.showText) {
                Text("开关")
            }.onChange(of: data.showText) { newValue in
                envData.showText = newValue
            }
            SubEnvironmentView()
        }
    }
}
//最外面,将环境对象传入
StateObservedEnvironment().environmentObject(TestBigData())

代码说明:

  1. ObservedObjectStateObject 可以往下穿透 给下级view去使用(比如代码里的 ScoreText)
  2. ObservedObjectStateObject 可以由swiftUI 外面传入。
  3. 属性变化触发 使用属性的代码块执行的时机, 是变化属性的代码作用域完毕后才触发 比如:
Button(“ShowOrHide”) {
        //修改该属性,会触发执行 使用该属性的代码
        showScore = !showScore    //此时不会同步触发 监听者的代码块
	print(“over”)  //该作用域代码执行完毕后,才会触发监听代码
}
  1. Published 属性的变化必须在主线程,因为UI刷新会同步执行。
  2. 可监听的对象A里,如果如果要让子属性为对象 或是数组对象的变化 也被监听到的话,那么该对象得是 struct(因为Published的成员变量只有变化时,才会触发主类的监听事件, 而struct 的属性变化时,会生成新的struct实例,由此触发主类的监听事件) 比如:
class TestBigData : ObservableObject {
    var realShow : Bool = true
    @Published var subData : SubTestData = SubTestData()
	@Published var subDataArr  = [SubTestData()]   
}
//必须是struct 
struct SubTestData {
    var subShowFlag : Bool = true
}

PS: 如果声明为class,当subShowFlag 变化时,实际上 TestBigData 的那两个Published 属性的值并没有变化,因此无法触发 监听事件