這一次我們來介紹一下 SwiftUI 里的屬性裝飾器@State, @Binding,@EnvironmentObject
@States
@frozen @propertyWrapper public struct State<Value> : DynamicProperty {
/// Initialize with the provided initial value.
public init(wrappedValue value: Value)
/// Initialize with the provided initial value.
public init(initialValue value: Value)
/// The current state value.
public var wrappedValue: Value { get nonmutating set }
/// Produces the binding referencing this state value
public var projectedValue: Binding<Value> { get }
}
@propertyWrapper 標(biāo)注和之前提到的 的 @_functionBuilder類似 例如下面@State修飾的代碼等同于 var showDetail = State(initialValue: false)侣背,會把使用過@State修飾器的屬性存儲到self上瞒滴,但是這個屬性和 View struct 是隔離的. 當(dāng)@State裝飾過的屬性發(fā)生了變化重抖,SwiftUI 會根據(jù)新的屬性值更新視圖
struct ContentView: View {
@State var showFavoritesOnly: Bool = false
//var showDetail = State(initialValue: false)
var body: some View {
VStack{
Button(action: {
self.showFavoritesOnly.toggle()
}) {
Text("Change")
}
if showFavoritesOnly {
Text("showFavoritesOnly")
}else{
Text("showAll)
}
}
}
}
@Binding
上面例子是showDetail這個屬性 button 不會自動修改,但有些控件是可以直接修改埋心,就可以把一個視圖的屬性傳至控件中鹤竭,例如下面DetailView里面的Toggle就可以挂脑,但是又不能直接的傳遞給控件藕漱,因為在 Swift 中值的傳遞形式是值類型傳遞方式,也就是傳遞出去的是一個拷貝過的值崭闲。但是通過 @Binding 修飾器修飾后肋联,用$也等同于@Binding,屬性變成了一個引用類型刁俭,傳遞變成了引用傳遞橄仍,這樣父子視圖的狀態(tài)就能關(guān)聯(lián)起來了。
struct ContentView: View {
@State var isFavorite: Bool
var body: some View {
VStack{
/// The binding value, as "unwrapped" by accessing `$foo` on a `@Binding` property.
DetailView(isFavorite: $isFavorite)
if isFavorite {
Text("isFavorite")
}else{
Text("NoFavorite")
}
}
}
}
struct DetailView: View {
@Binding var isFavorite: Bool
var body: some View {
Toggle(isOn: $isFavorite) {
Text("Change Favorite")
}
}
}
在 DetailView 視圖里用 @Binding 修飾 isFavorite 屬性, 在傳遞屬性是使用 $ 來傳遞 isFavorite 屬性的引用牍戚,這樣 DetailView 視圖就能讀寫父視圖 ContentView 里的狀態(tài)值了侮繁,并且值發(fā)生了修改 SwiftUI 會更新 ContentView 和 DetailView 視圖
@EnvironmentObject
從名字上可以看出,這個修飾器是針對全局環(huán)境的如孝。通過它宪哩,我們可以避免在初始 View 時創(chuàng)建 ObservableObject, 而是從環(huán)境中獲取 ObservableObject
@Environment
SwiftUI 本身就有很多系統(tǒng)默認(rèn)設(shè)定,我們可以使用@Environment 來獲取到它們
@Environment(.editMode) var mode
@Environment(.calendar) var calendar: Calendar
@Environment(.locale) var locale: Locale
@Environment(.colorScheme) var colorScheme: ColorScheme
使用系統(tǒng)的還可以得到很多好處第晰,例如你使用EditMode 就可以利用下面這些屬性來完成一個編輯頁面
public enum EditMode {
/// The view content cannot be edited.
case inactive
/// The view is in a temporary edit mode.
///
/// The definition of temporary might vary by platform or specific control.
/// As an example, temporary edit mode may be engaged over the duration of
/// a swipe gesture.
case transient
/// The view content can be edited.
case active
/// Indicates whether a view is being edited.
public var isEditing: Bool { get }
/// Returns a Boolean value indicating whether two values are equal.
///
/// Equality is the inverse of inequality. For any values `a` and `b`,
/// `a == b` implies that `a != b` is `false`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: Another value to compare.
}
你可以監(jiān)聽各種狀態(tài)
import SwiftUI
struct ProfileHost: View {
@Environment(\.editMode) var mode
@State var draftProfile = Profile.default
@EnvironmentObject var userData: UserData
var body: some View {
VStack(alignment: .leading, spacing: 20) {
HStack {
if self.mode?.wrappedValue == .active {
Button("Cancel") {
self.draftProfile = self.userData.profile
self.mode?.animation().wrappedValue = .inactive
}
}
Spacer()
EditButton()
}
//
if self.mode?.wrappedValue == .inactive {
ProfileSummary(profile: draftProfile)
} else {
ProfileEditor(profile: $draftProfile)
.onAppear {
self.draftProfile = self.userData.profile
}
.onDisappear {
self.userData.profile = self.draftProfile
}
}
}
.padding()
}
}
struct ProfileHost_Previews: PreviewProvider {
static var previews: some View {
ProfileHost()
}
}