屬性包裝、投射屬性
- 由
@
修飾的屬性稱為屬性包裝(Property Wrapper)。 @State
、@Binding
、@ObjectBinding
、@EnvironmentObject
都是@propertyWrapper
修飾的struct
。- 對一個由
@
修飾的屬性,在它前面使用$
取得的值,被稱為投射屬性(Projection Property)。 - 並不是所有的
@
屬性都提供$
的投射訪問方式。
@State
@State
修飾的屬性會被自動轉換為一對setter
和getter
- 對這個屬性進行賦值的操作,它的
body
會被再次調用,進行View
的刷新。 - 但由於是
value type
,在不同物件傳遞時透過複製值,因此經過不同層級底層改變並無法更新頂層的值。 - 訪問
@State
修飾的屬性,所有調用觸發的都是wrappedValue
。 - 使用
$
訪問屬性,取得的是projectedValue
,為一個Binding
類型的值。
SwiftUI 中 State
定義的關鍵部分
@propertyWrapper
public struct State<Value>: DynamicViewProperty, BindingConvertible {
public var value: Value { get nonmutating set }
public var wrappedValue: Value { get nonmutating set }
public var projectedValue: Binding<Value> { get }
init(initialValue value: Value)
}
範例:點擊按鈕數字累加點擊次數
struct ContentView: View {
@State private var count: Int = 0
var body: some View {
Button {
self.count += 1
} label: {
Circle()
.frame(width: 150, height: 150, alignment: .center)
.foregroundColor(.blue)
.overlay {
Text("\(count)")
.font(.system(size:72, weight: .bold))
.foregroundColor(.white)
}
}
}
}
@Binding
- 和
@State
類似,也是對屬性的修飾,但將value type
轉換為reference type
。 - 對
@Binding
修飾的屬性進行賦值,改變的是其參考,因此可以在不同物件傳遞。
範例:同上(將按鈕定義為CounterButton)
CounterButton
中宣告count
變數以@Binding
修飾,在ContentView
以projectedValue
將$counter
傳遞至CounterButton
,當點擊按鈕count
值改變,畫面改變,counter
亦跟著改變。
print(counter) 印出結果皆為 1, 2, 3…
- 若
CounterButton
中宣告count
變數以@State
修飾,在ContentView
以wrappedValue
將counter
傳遞至CounterButton
,當點擊按鈕count
值改變,畫面改變,但counter
不會跟著改變。
print(counter) 印出結果皆為 0
struct ContentView: View {
@State private var counter: Int = 0
var body: some View {
CounterButton(count: $counter) {
print(counter)
}
}
}
struct CounterButton: View {
@Binding var count: Int
var body: some View {
Button {
self.count += 1
} label: {
Circle()
.frame(width: 150, height: 150, alignment: .center)
.foregroundColor(.blue)
.overlay {
Text("\(count)")
.font(.system(size:72, weight: .bold))
.foregroundColor(.white)
}
}
}
}