在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    SwiftUI 狀態(tài)管理系統(tǒng)指南

    前言

    SwiftUI與蘋果之前的UI框架的區(qū)別不僅僅在于如何定義視圖和其他UI組件,還在于如何在整個使用它的應用程序中管理視圖層級的狀態(tài)。

    SwiftUI沒有使用委托、數(shù)據源或任何其他在UIKit和AppKit等命令式框架中常見的狀態(tài)管理模式,而是配備了一些屬性包裝器[1],使我們能夠準確地聲明我們的數(shù)據如何被我們的視圖觀察、渲染和改變。

    本周,讓我們仔細看看這些屬性包裝器中的每一個,它們之間的關系,以及它們如何構成SwiftUI整體狀態(tài)管理系統(tǒng)的不同部分。

    屬性狀態(tài)

    由于SwiftUI主要是一個UI框架(盡管它也開始獲得用于定義更高層次結構(如應用程序和場景)的API),其聲明式設計不一定需要影響應用程序的整個模型和數(shù)據層——而只是直接綁定到我們各種視圖的狀態(tài)。

    例如,假設我們正在開發(fā)一個SignupView,使用戶能夠通過輸入用戶名和電子郵件地址在應用程序中注冊一個新賬戶。我們將使用這兩個值形成一個用戶模型,并將其傳遞給一個閉包:

    struct SignupView: View { var handler: (User) -> Void var username = “” var email = “” var body: some View { … }}

    由于這三個屬性中只有兩個——username和email——實際上會被我們的視圖修改,而且這兩個狀態(tài)可以保持私有,我們將使用SwiftUI的State屬性包裝器來標記它們——像這樣:

    struct SignupView: View { var handler: (User) -> Void @State private var username = “” @State private var email = “” var body: some View { … }}

    這樣做將自動在這兩個值和我們的視圖本身之間建立一個連接——這意味著我們的視圖將在每次改變這兩個值的時候被重新渲染。在我們的主體中,我們將把這兩個屬性分別綁定到一個相應的TextField上,以使它們可以被用戶編輯:

    struct SignupView: View { var handler: (User) -> Void @State private var username = “” @State private var email = “” var body: some View { VStack { TextField(“Username”, text: $username) TextField(“Email”, text: $email) Button( action: { self.handler(User( username: self.username, email: self.email )) }, label: { Text(“Sign up”) } ) } .padding() }}

    因此,State 被用來表示SwiftUI視圖的內部狀態(tài),并在該狀態(tài)被改變時自動使視圖更新。因此,最常見的做法是將State屬性包裝器保持為私有,這可以確保它們只在該視圖的主體內被改變(試圖在其他地方改變它們實際上會導致運行時崩潰)。

    雙向綁定

    看一下上面的代碼樣本,我們將每個屬性傳入其TextField 的方式是在這些屬性名稱前加上$ 。這是因為我們不只是將普通的String 值傳入這些文本字段,而是與我們的State包裝的屬性本身綁定。

    為了更詳細地探討這意味著什么,讓我們現(xiàn)在假設我們想創(chuàng)建一個視圖,讓我們的用戶編輯他們最初在注冊時輸入的個人資料信息。由于我們現(xiàn)在要修改外部狀態(tài)值,而不僅僅是私人狀態(tài)值,所以這次我們將username和email 屬性標記為Bingding:

    struct ProfileEditingView: View { @Binding var username: String @Binding var email: String var body: some View { VStack { TextField(“Username”, text: $username) TextField(“Email”, text: $email) } .padding() }}

    最酷的是,綁定不僅僅局限于單一的內置值,比如字符串或整數(shù),而是可以用來將任何Swift值綁定到我們的一個視圖中。例如,我們可以將用戶模型本身傳遞給ProfileEditingView ,而不是傳遞兩個單獨的username和email:

    struct ProfileEditingView: View { @Binding var user: User var body: some View { VStack { TextField(“Username”, text: $user.username) TextField(“Email”, text: $user.email) } .padding() }}就像我們在將State和

    就像我們在將State和Binding 包裝的屬性傳入各種TextField 實例時用$ 作為前綴一樣,我們在將任何State 值連接到我們自己定義的Binding屬性時也可以做同樣的事情。

    例如,這里有一個ProfileView 的實現(xiàn),它使用一個Stage 包裝屬性來跟蹤一個用戶模型,然后在將上述ProfileEditingView 的實例作為工作表呈現(xiàn)時,將該模型傳遞一個綁定——這將自動同步用戶對該原始State屬性值的任何改變:

    struct ProfileView: View { @State private var user = User.load() @State private var isEditingViewShown = false var body: some View { VStack(alignment: .leading, spacing: 10) { Text(“Username: “) .foregroundColor(.secondary) + Text(user.username) Text(“Email: “) .foregroundColor(.secondary) + Text(user.email) Button( action: { self.isEditingViewShown = true }, label: { Text(“Edit”) } ) } .padding() .sheet(isPresented: $isEditingViewShown) { VStack { ProfileEditingView(user: self.$user) Button( action: { self.isEditingViewShown = false }, label: { Text(“Done”) } ) } } }}

    請注意,我們也可以通過給一個State 包裝的屬性分配一個新的值來改變它——比如我們在 “Done “按鈕的動作處理程序中把isEditingViewShown 設置為false。

    因此,一個Binding 標記的屬性在給定的視圖和定義在該視圖之外的狀態(tài)屬性之間提供了一個雙向的連接,而Statr和Binding 包裝的屬性都可以通過在其屬性名前加上$來作為綁定物傳遞。

    觀察對象

    State和Bingding的共同點是,它們處理的是在SwiftUI視圖層次結構本身中管理的值。然而,雖然建立一個將所有的狀態(tài)都保存在其各種視圖中的應用程序是肯定可行的,但從架構和關注點分離的角度來看,這通常不是一個好主意,而且很容易導致我們的視圖變得相當龐大和復雜。

    值得慶幸的是,SwiftUI還提供了一些機制,使我們能夠將外部模型對象連接到我們的各種視圖。其中一個機制是ObservableObject 協(xié)議,當它與ObservedObject屬性包裝器結合時,我們可以設置與我們視圖層之外管理的引用類型的綁定。

    作為一個例子,讓我們更新上面定義的ProfileView ——通過將管理User 模型的責任從視圖本身轉移到一個新的、專門的對象中?,F(xiàn)在,我們可以用許多不同的方式來描述這樣一個對象,但由于我們正在尋找創(chuàng)建一個類型來控制我們的一個模型的實例——讓我們把它變成一個符合SwiftUI的ObservableObject協(xié)議的模型控制器[2]:

    class UserModelController: ObservableObject { @Published var user: User …}

    Published屬性包裝器用于定義對象的哪些屬性在被修改時應讓觀察通知被觸發(fā)。

    有了上面的類型,現(xiàn)在讓我們回到ProfileView ,讓它觀察新的UserModelController 的實例,作為一個ObservedObject ,而不是用一個State 屬性包裝器來跟蹤我們的用戶模型。最重要的是,我們仍然可以很容易地將這個模型綁定到我們的ProfileEditingView 上,就像以前一樣,因為ObservedObject屬性包裝器也可以轉換為綁定:

    struct ProfileView: View { @ObservedObject var userController: UserModelController @State private var isEditingViewShown = false var body: some View { VStack(alignment: .leading, spacing: 10) { Text(“Username: “) .foregroundColor(.secondary) + Text(userController.user.username) Text(“Email: “) .foregroundColor(.secondary) + Text(userController.user.email) Button( action: { self.isEditingViewShown = true }, label: { Text(“Edit”) } ) } .padding() .sheet(isPresented: $isEditingViewShown) { VStack { ProfileEditingView(user: self.$userController.user) Button( action: { self.isEditingViewShown = false }, label: { Text(“Done”) } ) } } }}

    然而,我們的新實現(xiàn)與之前使用的基于狀態(tài)的實現(xiàn)之間的一個重要區(qū)別是,我們的UserModelController 現(xiàn)在需要作為初始化器的一部分被注入到ProfileView中。

    除了 “迫使 “我們在代碼庫中建立一個更明確的依賴關系圖之外,原因是一個標有ObservedObject的屬性并不意味著對這個屬性所指向的對象有任何形式的所有權。

    因此,雖然下面的內容在技術上可能會被編譯,但最終會導致運行時的問題——因為當我們的視圖在更新時被重新創(chuàng)建,UserModelController實例可能會被刪除(因為我們的視圖現(xiàn)在是它的主要所有者):

    struct ProfileView: View { @ObservedObject var userController = UserModelController.load() …}

    重要的是要記住: SwiftUI視圖不是對正在屏幕上渲染的實際UI組件的引用,而是描述我們的UI的輕量級值——因此它們沒有像UIView實例那樣的生命周期。

    為了解決上述問題,蘋果在iOS 14和macOS Big Sur中引入了一個新的屬性包裝器,名為StateObject 。標記為StateObject 的屬性與ObservedObject的行為完全相同——此外,SwiftUI將確保存儲在此類屬性中的任何對象不會因為框架在重新渲染視圖時重新創(chuàng)建新實例而被意外釋放:

    struct ProfileView: View { @StateObject var userController = UserModelController.load() …}

    盡管從技術上來說,從現(xiàn)在開始可以只使用StateObject ——我仍然建議在觀察外部對象時使用ObservedObject ,而在處理視圖本身擁有的對象時只使用StateObject 。把StateObject和ObservedObject 看作是State和Binding的參考類型,或者SwiftUI版本的強和弱屬性。

    觀察和修改環(huán)境變量

    最后,讓我們來看看SwiftUI的環(huán)境系統(tǒng)如何被用來在兩個互不直接連接的視圖之間傳遞各種狀態(tài)。盡管在一個父視圖和它的一個子視圖之間創(chuàng)建綁定通常很容易,但在整個視圖層次結構中傳遞某個對象或值可能相當麻煩——而這正是環(huán)境變量旨在解決的問題類型。

    有兩種主要的方法來使用SwiftUI的環(huán)境。一種是首先在想要檢索給定對象的視圖中定義一個EnvironmentObject 包裝的屬性——例如像這個ArticleView 如何檢索一個包含顏色信息的Theme對象:

    struct ArticleView: View { @EnvironmentObject var theme: Theme var article: Article var body: some View { VStack(alignment: .leading) { Text(article.title) .foregroundColor(theme.titleTextColor) Text(article.body) .foregroundColor(theme.bodyTextColor) } }}

    然后,我們必須確保在我們的視圖的某一個父類中提供我們的環(huán)境對象(在這種情況下是一個Theme 實例),然后SwiftUI會處理其余的事情。這是通過使用environmentalObject修飾符完成的,例如,像這樣:

    struct RootView: View { @ObservedObject var theme: Theme @ObservedObject var articleLibrary: ArticleLibrary var body: some View { ArticleListView(articles: articleLibrary.articles) .environmentObject(theme) }}

    請注意,我們不需要將上述修改器應用于將使用我們的環(huán)境對象的確切視圖——我們可以將其應用于我們的層次結構中任何在其之上的視圖。

    使用 SwiftUI 環(huán)境系統(tǒng)的第二種方式是定義一個自定義的EnvironmentKey ——然后它可以被用來向內置的 EnvironmentValues 類型分配和檢索值:

    struct ThemeEnvironmentKey: EnvironmentKey { static var defaultValue = Theme.default}extension EnvironmentValues { var theme: Theme { get { self[ThemeEnvironmentKey.self] } set { self[ThemeEnvironmentKey.self] = newValue } }}

    有了上述內容,我們現(xiàn)在可以使用Enviroment 屬性包裝器(而不是EnvironmentObject )來標記我們視圖的theme屬性,并傳入我們希望檢索的環(huán)境鍵的鍵值路徑:

    struct ArticleView: View { @Environment(.theme) var theme: Theme var article: Article var body: some View { VStack(alignment: .leading) { Text(article.title) .foregroundColor(theme.titleTextColor) Text(article.body) .foregroundColor(theme.bodyTextColor) } }}

    上述兩種方法的一個明顯區(qū)別是,基于鍵的方法要求我們在編譯時定義一個默認值,而基于環(huán)境對象EnvironmentObject的方法則假設在運行時提供這樣一個值(如果不這樣做將導致崩潰)。

    小結

    SwiftUI管理狀態(tài)的方式絕對是該框架最有趣的方面之一,它可能需要我們稍微重新思考數(shù)據在應用中的傳遞方式——至少在涉及到將被我們的UI直接消費和修改的數(shù)據時是這樣。

    我希望這篇指南能成為一個很好的方式來概述SwiftUI的各種狀態(tài)處理機制,盡管一些更具體的API被遺漏了,這篇文章中強調的概念應該涵蓋了所有基于SwiftUI的狀態(tài)處理的絕大多數(shù)用例。

    感謝你的閱讀!

    鄭重聲明:本文內容及圖片均整理自互聯(lián)網,不代表本站立場,版權歸原作者所有,如有侵權請聯(lián)系管理員(admin#wlmqw.com)刪除。
    用戶投稿
    上一篇 2022年6月22日 15:20
    下一篇 2022年6月22日 15:21

    相關推薦

    • ios手游模擬器(手游模擬器ios)

      本文主要講的是ios手游模擬器,以及和手游模擬器ios相關的知識,如果覺得本文對您有所幫助,不要忘了將本文分享給朋友。 哪個iOS模擬器能多開手游賬號?可以推薦個好用的模擬器給我嗎…

      2022年11月27日
    • 短視頻策劃內容的3個要點(短視頻策劃內容怎么做)

      短視頻在制作時,內容框架非常重要。如果直奔主題,然后結束,聚卓告訴你,這樣的短視頻已經過時了?,F(xiàn)在的短視頻需要框架的,但不是任何框架,它需要一種易于理解和消化的框架。而且,現(xiàn)在大多…

      2022年11月27日
    • 美團第三季度實現(xiàn)營收626億元,即時配送訂單量增至50億筆

      新京報訊(記者秦勝南)11月25日,美團發(fā)布業(yè)績公告顯示,第三季度營收為626億元,較去年同比增長28.2%,凈利潤為12.2億元。第三季度,美團即時配送訂單數(shù)增長至50億筆。截至…

      2022年11月27日
    • 個人怎么做抖音帶貨(個人做抖音帶貨能賺錢嗎)

      抖音如今是大家很熟悉的短視頻平臺,不過現(xiàn)在的抖音卻不只是短視頻那么簡單,它的功能非常豐富,其中一個就是可以帶貨,相信很多小伙伴都有在抖音上買過東西,抖音如今的變現(xiàn)能力也是不容小覷的…

      2022年11月25日
    • 《寶可夢朱紫》獒教父屬性是什么?獒教父屬性一覽

      寶可夢朱紫里獒教父是一只很強的寶可夢,很多玩家不清楚獒教父的屬性是什么樣的,下面就給大家?guī)韺毧蓧糁熳祥峤谈笇傩砸挥[,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 獒教父屬性一覽…

      2022年11月25日
    • 《寶可夢朱紫》太晶化強力寶可夢推薦 太晶化哪些寶可夢最強?

      寶可夢朱紫游戲中寶可夢種類繁多,不過有的寶可夢比較強,有的稍弱一些,那么太晶化化哪些寶可夢最強呢,為了便于大家更好的體驗游戲,這里給大家?guī)砹藢毧蓧糁熳咸Щ瘡娏毧蓧敉扑],一起來…

      2022年11月25日
    • 寶可夢朱紫四大天王屬性怎么樣 四大天王屬性數(shù)值介紹

      寶可夢朱紫四大天王屬性如何?四大天王的屬性數(shù)值玩家們還是挺好奇的,想要了解四大天王屬性的可以看看下面小編的介紹,小編會把四大天王的屬性數(shù)值全都分享在下面,各位趕緊來小編這里多了解一…

      2022年11月25日
    • 寶可夢朱紫皮卡丘多少級進化 皮卡丘進化條件攻略

      寶可夢朱紫皮卡丘進化條件是什么?皮卡丘怎么進化?作為最受歡迎的電屬性寶可夢,大家都很想知道皮卡丘進化的方法,今天小編這就在下面的攻略中分享皮卡丘進化條件,各位可以趕緊來小編這里了解…

      2022年11月25日
    • 小紅書平臺的一些機制及玩法詳解(小紅書玩法有哪些)

      關于小紅書 一:小紅書平臺的一些機制 1. 筆記內容的CES評分機制 2. 筆記流量入口與長尾效應 二:小紅書優(yōu)質筆記的特點(分維度、類型分析) 1.筆記的本身架構組成 維度 2.…

      2022年11月25日
    • 百度關鍵詞快速排名的4大原理解析(百度怎么刷關鍵詞)

      近期百度公告驚雷算法2.0,升級之快還是第一次吧,看來百度對于刷點擊行為是零容忍了。之前尹華峰SEO技術博客介紹過一篇如何使用刷點擊工具,其實市面上有很多這類SEO快速排名的軟件,…

      2022年11月25日

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時間:周一至周五,10:30-18:30,節(jié)假日休息