SwiftUI 中的屬性包裝器

SwiftUI中,包裝器詳細(xì)說(shuō)明

下面羅列17個(gè)包裝器的說(shuō)明。
@AppStorage 從 UserDefaults 讀取和寫(xiě)入值。
@Binding 引用另一個(gè)視圖擁有的值類型數(shù)據(jù)。在本地更改綁定也會(huì)更改遠(yuǎn)程數(shù)據(jù)。
@Environment 允許我們從系統(tǒng)讀取數(shù)據(jù),例如配色方案,可訪問(wèn)性選項(xiàng)和特征集,但是如果需要,您可以在此處添加自己的密鑰。
@EnvironmentObject 讀取我們放入環(huán)境中的共享對(duì)象。
@FetchRequest 為特定實(shí)體啟動(dòng)核心數(shù)據(jù)獲取請(qǐng)求。
@FocusedBinding 旨在監(jiān)視鍵窗口中的值,例如當(dāng)前選定的文本字段。
@FocusedValue 是 @FocusedBinding 的簡(jiǎn)單版本,不會(huì)為您解開(kāi)綁定值。
@GestureState 存儲(chǔ)與當(dāng)前正在進(jìn)行的手勢(shì)關(guān)聯(lián)的值,例如您滑動(dòng)了多遠(yuǎn),除非在手勢(shì)停止時(shí)它將重置為其默認(rèn)值。
@Namespace 創(chuàng)建一個(gè)動(dòng)畫(huà)名稱空間,以允許匹配的幾何效果,該效果可以由其他視圖共享。
@NSApplicationDelegateAdaptor 用于創(chuàng)建類并將其注冊(cè)為 macOS 應(yīng)用程序的應(yīng)用程序委托。
@ObservedObject 引用符合 ObservableObject 協(xié)議的外部類的實(shí)例。
@Published 附加到 ObservableObject 內(nèi)的屬性,并告訴 SwiftUI 它應(yīng)該在更改此屬性時(shí)刷新使用此屬性的所有視圖。
@ScaledMetric 會(huì)讀取用戶的動(dòng)態(tài)類型設(shè)置,并根據(jù)您提供的原始值向上或向下縮放數(shù)字。
@SceneStorage 使我們可以保存和還原少量數(shù)據(jù)以進(jìn)行狀態(tài)還原。
@State 允許我們?cè)谝晥D本地操作少量的值類型數(shù)據(jù)。
@StateObject 用于存儲(chǔ)符合 ObservableObject 協(xié)議的引用類型數(shù)據(jù)的新實(shí)例。
@UIApplicationDelegateAdaptor 用于創(chuàng)建一個(gè)類并將其注冊(cè)為iOS應(yīng)用程序的應(yīng)用程序委托。


SwiftUI屬性包裝器.png

@State、@Binding、@ObservedObject和@EnvironmentObject是幾個(gè)重要的屬性包裝器,它們各自在數(shù)據(jù)管理和視圖更新中扮演著不同的角色。有的配合使用,下面給出幾個(gè)例子介紹下用法

State

@State用于在視圖中聲明可變的狀態(tài)屬性,并自動(dòng)更新視圖。當(dāng)@State修飾的屬性值發(fā)生變化時(shí),SwiftUI會(huì)自動(dòng)重新渲染視圖以反映這些變化。
在這個(gè)例子中,count是一個(gè)@State屬性,它控制著一個(gè)計(jì)數(shù)器的值。當(dāng)按鈕被點(diǎn)擊時(shí),count的值會(huì)增加,并且由于count是@State屬性,SwiftUI會(huì)自動(dòng)更新視圖以顯示新的計(jì)數(shù)值。

struct ContentView: View {
    @State private var count: Int = 0
    
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint) 
            Text("Hello, SwiftUI \(count)")
            Button(action: {
                count += 1
            }, label: {
                Text("Increment")
            })  
        }
        .padding()
    }
}

Binding

@Binding用于在不同視圖之間傳遞和共享可讀寫(xiě)的值。它創(chuàng)建了一個(gè)對(duì)屬性的引用,以便多個(gè)視圖可以共享同一份數(shù)據(jù),并且對(duì)數(shù)據(jù)的更改會(huì)在所有引用的地方生效。

在這個(gè)例子中,ParentView有一個(gè)@State屬性isChildViewVisible,它控制ChildView的可見(jiàn)性。通過(guò)@Binding,ChildView能夠訪問(wèn)并修改isChildViewVisible的值,從而控制自己的顯示狀態(tài)。

struct ParentView: View {
    
    @State private var isChildViewVisible = false
    var body: some View {
        VStack {
            Toggle(isOn: $isChildViewVisible) {
                Text("Show Child View")
            }
            if isChildViewVisible {
                ChildView(isVisible: $isChildViewVisible)
            }
        }
    }
}

struct ChildView: View {
    @Binding var isVisible: Bool
    var body: some View {
        Text("Child View")
            .padding()
        Button(action: {
            isVisible = false
        }) {
            Text("Hide")
        }
    }
}

StateObject

@StateObject
@StateObject用于在SwiftUI視圖中創(chuàng)建并管理一個(gè)可觀察的對(duì)象(通常是實(shí)現(xiàn)了ObservableObject協(xié)議的類)。這個(gè)對(duì)象會(huì)在視圖的生命周期內(nèi)保持唯一,即使視圖被重新渲染。
在這個(gè)例子中,ViewModel是一個(gè)實(shí)現(xiàn)了ObservableObject協(xié)議的類,它有一個(gè)@Published屬性text。ContentView使用@StateObject來(lái)創(chuàng)建并管理ViewModel的實(shí)例。當(dāng)按鈕被點(diǎn)擊時(shí),text屬性的值會(huì)更新,并且由于ViewModel是可觀察的,ContentView會(huì)自動(dòng)重新渲染以顯示新的文本。

class ViewModel: ObservableObject {  
    @Published var text = "Hello, World!"  
}  
  
struct ContentView: View {  
    @StateObject var viewModel = ViewModel()  

    var body: some View {  
        VStack {  
            Text(viewModel.text)  
            Button(action: {  
                viewModel.text = "Updated Text!"  
            }) {  
                Text("Update Text")  
            }  
        }  
    }  
}  

ObservedObject

@ObservedObject用于觀察引用類型對(duì)象的變化,并在對(duì)象更改時(shí)更新視圖。它通常用于管理外部對(duì)象的狀態(tài),并在該對(duì)象發(fā)生變化時(shí)自動(dòng)刷新視圖。
在這個(gè)例子中,UserData是一個(gè)遵循ObservableObject協(xié)議的類,它有一個(gè)@Published屬性name。ContentView使用@ObservedObject來(lái)觀察UserData對(duì)象,當(dāng)name屬性發(fā)生變化時(shí),ContentView會(huì)自動(dòng)更新以顯示新的名稱。

//必須要集成 ObservableObject
class UserData: ObservableObject {
  //需要監(jiān)聽(tīng)的屬性需要  Published
    @Published var name: String = ""
}
  
struct UserDataContentView: View {
    @ObservedObject var user = UserData()
    var body: some View {
        VStack {
            //user.name 綁定到TextField上
            TextField("Enter your name", text: $user.name)
                .textFieldStyle(.roundedBorder)
            Text("Hello, \(user.name)!")
        }
    }
}

AppStorage

@AppStorage 是 SwiftUI 中的一個(gè)屬性包裝器,它允許開(kāi)發(fā)者將屬性的值存儲(chǔ)在 UserDefaults 中,并在應(yīng)用程序的不同啟動(dòng)之間保留這些值。這對(duì)于存儲(chǔ)用戶偏好設(shè)置等非常有用。

聲明和用法
要使用 @AppStorage,你需要將其附加到你的屬性上,并指定一個(gè)鍵(key)和一個(gè)默認(rèn)值。例如:
@AppStorage("user_name") var username: String = "Guest"
在這個(gè)例子中,username 屬性使用 @AppStorage 進(jìn)行存儲(chǔ)和檢索,它的鍵為 "user_name",默認(rèn)值為 "Guest"。如果 UserDefaults 中已經(jīng)存在 "user_name" 這個(gè)鍵對(duì)應(yīng)的值,那么 username 將被初始化為那個(gè)值;如果不存在,則使用默認(rèn)值 "Guest"。

struct ContentView: View {  
    @AppStorage("user_name") var username: String = "Guest"  
  
    var body: some View {  
        VStack {  
            Text("Hello, \(username)!")  
            Button("Change Username") {  
                username = "NewUser"  
            }  
        }  
    }  
}

在這個(gè)示例中,username 屬性使用 @AppStorage 進(jìn)行存儲(chǔ)和檢索。當(dāng)用戶點(diǎn)擊 "Change Username" 按鈕時(shí),username 的值會(huì)更改為 "NewUser",并且在應(yīng)用程序的不同啟動(dòng)之間保留這個(gè)更改。

注意事項(xiàng)

  • 數(shù)據(jù)類型匹配:存儲(chǔ)的數(shù)據(jù)類型必須與屬性的聲明類型匹配。@AppStorage 支持基本數(shù)據(jù)類型(如 String、Int、Bool 等),但不能直接用于自定義類或結(jié)構(gòu)體。
  • 默認(rèn)值:如果不設(shè)置默認(rèn)值,則變量的類型為可選值類型(如 String?)。
  • UserDefaults:@AppStorage 底層使用 UserDefaults 來(lái)存儲(chǔ)數(shù)據(jù)。因此,對(duì) UserDefaults 的操作將直接影響對(duì)應(yīng)的 @AppStorage。
  • 數(shù)據(jù)安全性:由于 UserDefaults 的數(shù)據(jù)相對(duì)容易提取,因此不要在其中保存與隱私有關(guān)的重要數(shù)據(jù)。
  • 數(shù)據(jù)同步:為了效率的考量,UserDefaults 中的數(shù)據(jù)在發(fā)生變化時(shí)并不會(huì)立即持久化,系統(tǒng)會(huì)在認(rèn)為合適的時(shí)機(jī)才將數(shù)據(jù)保存在硬盤(pán)中。因此,可能發(fā)生數(shù)據(jù)不能完全同步的情況,嚴(yán)重時(shí)有數(shù)據(jù)徹底丟失的可能。

EnvironmentObject

@EnvironmentObject用于在應(yīng)用程序中共享和訪問(wèn)全局?jǐn)?shù)據(jù)。它允許你在應(yīng)用程序的任何視圖中訪問(wèn)和使用共享數(shù)據(jù),而無(wú)需手動(dòng)傳遞它們。
在這個(gè)例子中,UserData是一個(gè)全局的ObservableObject,它存儲(chǔ)了用戶的信息。ContentView使用@EnvironmentObject來(lái)訪問(wèn)UserData對(duì)象,并在視圖中顯示用戶名稱。在MyApp中,userData對(duì)象被創(chuàng)建并通過(guò).environmentObject方法注入到ContentView中,使得ContentView能夠訪問(wèn)到全局的用戶數(shù)據(jù)。

class UserData: ObservableObject {
    @Published var name: String = "John"
}

struct ContentView: View {
    @StateObject var userData = UserData()
    var body: some View {
        VStack {
            UserDataContentView().environmentObject(userData)
        }
        .padding()
    }
}

struct UserDataContentView: View {
    @EnvironmentObject var userData: UserData
    var body: some View {
        VStack {
            Text("Hello, \(userData.name)!")
        }
    }
}

FocusState

@FocusState用于管理視圖中的焦點(diǎn)狀態(tài)。在需要鍵盤(pán)輸入或特定交互的視圖中,@FocusState可以幫助開(kāi)發(fā)者確定哪個(gè)視圖當(dāng)前具有焦點(diǎn),并相應(yīng)地調(diào)整UI。
由于SwiftUI的TextField默認(rèn)不支持直接的焦點(diǎn)API,上述示例中的.focused($isFocused)是假設(shè)性的,用于說(shuō)明@FocusState的概念。在實(shí)際應(yīng)用中,你可能需要借助UIViewRepresentable或其他技術(shù)來(lái)實(shí)現(xiàn)焦點(diǎn)管理。

struct ContentView: View {  
    @FocusState var isFocused: Bool  
  
    var body: some View {  
        VStack {  
            TextField("Type something...", text: .constant(""))  
                .focused($isFocused)  
            Button(action: {  
                isFocused = !isFocused  
            }) {  
                Text(isFocused ? "Lose Focus" : "Gain Focus")  
            }  
        }  
    }  
}  
  
// 注意:由于TextField在SwiftUI中默認(rèn)不支持直接的focus API,這里使用了偽代碼來(lái)展示概念。  
// 在實(shí)際應(yīng)用中,你可能需要使用自定義的UIViewRepresentable來(lái)包裝UIKit的UITextField或使用其他方法來(lái)實(shí)現(xiàn)焦點(diǎn)管理。

Environment

@Environment 屬性包裝器
雖然@Environment屬性包裝器在SwiftUI中不如@EnvironmentObject常見(jiàn),但它用于訪問(wèn)系統(tǒng)級(jí)的環(huán)境值,如布局方向、顏色方案等。這些值由SwiftUI框架自動(dòng)管理,并在需要時(shí)傳遞給視圖。
@Environment是獲取系統(tǒng)環(huán)境變量的。例如isEnabled,editMode,presentationMode,horizontalSizeClass,verticalSizeClass等。
示例(假設(shè)性代碼,因?yàn)锧Environment通常不直接用于訪問(wèn)這些值,而是使用其他方式,如@Environment(.keyPath)):

struct MyView: View {  
    // 假設(shè)的代碼,實(shí)際中不會(huì)這樣使用@Environment直接訪問(wèn)系統(tǒng)值  
    // @Environment var layoutDirection: LayoutDirection // 這不是有效的用法  
  
    // 正確的用法是使用@Environment和鍵路徑  
    @Environment(\.layoutDirection) var layoutDirection: LayoutDirection  
  
    var body: some View {  
        // 根據(jù)布局方向調(diào)整UI  
        if layoutDirection == .leftToRight {  
            // ...  
        } else {  
            // ...  
        }  
    }  
}

請(qǐng)注意,上面的@Environment var layoutDirection: LayoutDirection示例是假設(shè)性的,因?yàn)锧Environment通常與鍵路徑一起使用(如@Environment(.layoutDirection)),而不是直接聲明變量類型。在SwiftUI中,你更可能會(huì)看到像@Environment(.layoutDirection) var layoutDirection: LayoutDirection這樣的用法。

總的來(lái)說(shuō),@EnvironmentObject是SwiftUI中用于在視圖之間共享可觀察對(duì)象的強(qiáng)大工具,而@Environment(與鍵路徑一起使用時(shí))則用于訪問(wèn)系統(tǒng)級(jí)的環(huán)境值。

參考: https://zhuanlan.zhihu.com/p/663955261

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容