SwiftUI學(xué)習(xí)100天(Day79 - 項目 16,第一部分)

原創(chuàng)鏈接:https://www.hackingwithswift.com/100/swiftui
以下內(nèi)容僅供學(xué)習(xí)參考:

現(xiàn)在你已經(jīng)跨過了使用 UIKit 的難關(guān),我們可以回到使用純 SwiftUI 的更快樂的地方——我想你現(xiàn)在會更加感激它!
在這個項目中,你將學(xué)習(xí) Swift 和 SwiftUI 的非常棒的功能組合;它確實有助于提高你的技能并讓你靈活地創(chuàng)建功能強大的應(yīng)用程序。是的,即使在 79 天到我們的 100 天,仍有許多新東西需要學(xué)習(xí)——正如 Lily Tomlin 所說,“通往成功的道路總是在建設(shè)中”!
今天你要完成三個主題,你將在其中了解自定義環(huán)境對象、選項卡視圖等。

熱門前景:簡介
在這個項目中,我們將構(gòu)建 Hot Prospects,這是一款用于跟蹤你在會議上遇到的人的應(yīng)用程序。你可能以前見過類似的應(yīng)用程序:它會顯示一個存儲你的與會者信息的二維碼,然后其他人可以掃描該代碼將你添加到他們的潛在線索列表中,以便以后跟進。
這聽起來可能很簡單,但在此過程中我們將介紹大量真正重要的新技術(shù):創(chuàng)建選項卡欄和上下文菜單、使用環(huán)境共享自定義數(shù)據(jù)、發(fā)送自定義更改通知等等。生成的應(yīng)用程序很棒,但你在此過程中學(xué)到的東西將特別有用!
與往常一樣,在我們開始實施我們的項目之前,我們有很多技術(shù)要介紹,所以請首先使用 App 模板創(chuàng)建一個新的 iOS 項目,并將其命名為 HotProspects。
讓我們開始吧!



使用@EnvironmentObject 從環(huán)境中讀取自定義值
你已經(jīng)了解了如何使用@State
單個視圖的本地狀態(tài),以及如何用@ObservedObject
讓我們將一個對象從一個視圖傳遞到另一個視圖以便我們可以共享它。好吧,@EnvironmentObject
更進一步:我們可以將一個對象放入環(huán)境中,這樣任何子視圖都可以自動訪問它。
想象一下,我們在一個應(yīng)用程序中有多個視圖,所有視圖都排成一條鏈:視圖 A 顯示視圖 B,視圖 B 顯示視圖 C,C 顯示 D,D 顯示 E。視圖 A 和 E 都想訪問同一個對象,但是要從 A 到 E,你需要經(jīng)過 B、C 和 D,而他們不關(guān)心那個對象。如果我們正在使用@ObservedObject
,
我們需要將我們的對象從每個視圖傳遞到下一個視圖,直到它最終到達可以使用它的視圖 E,這很煩人,因為 B、C 和 D 不關(guān)心它。視圖@EnvironmentObject?
A 可以將對象放入環(huán)境中,視圖 E 可以從環(huán)境中讀取對象,而視圖 B、C 和 D 不必知道發(fā)生了什么——這樣更好。
在我向你展示一些代碼之前,還有最后一件事:環(huán)境對象使用ObservableObject
,
你已經(jīng)了解的相同協(xié)議,并且 SwiftUI 將自動確保共享相同環(huán)境對象的所有視圖在更改時得到更新。
好的,讓我們看一些代碼,展示如何使用環(huán)境對象在兩個視圖之間共享數(shù)據(jù)。首先,這是我們可以使用的一些基本數(shù)據(jù):
如你所見,它使用@MainActor
,?ObservableObject
, 和@Published
就像我們之前學(xué)到的一樣——你積累的所有知識都會繼續(xù)得到回報。
接下來我們可以定義兩個 SwiftUI 視圖來使用我們的新類。這些將使用@EnvironmentObject
屬性包裝器表示此數(shù)據(jù)的值來自環(huán)境而不是在本地創(chuàng)建:
該@EnvironmentObject
屬性包裝器將自動在環(huán)境中查找實例User
,并將找到的任何內(nèi)容放入user
屬性中。如果它在環(huán)境中找不到User
,你的代碼就會崩潰,所以請小心。
我們現(xiàn)在可以將這兩個視圖放在一個地方,并發(fā)送一個User
實例供它們使用:
這就是讓我們的代碼正常工作所需的一切——你現(xiàn)在可以運行該應(yīng)用程序并更改文本字段以查看其值出現(xiàn)在下面的文本視圖中。當然,我們可以在單個視圖中表示這一點,但這樣你就可以準確地看到使用環(huán)境對象時通信的無縫程度。
現(xiàn)在,這是聰明的部分。ContentView
嘗試將
:body
的屬性重寫為
你會發(fā)現(xiàn)它的工作原理是一樣的。我們現(xiàn)在放置user
在ContentView
的環(huán)境中
,但因為EditView
和
DisplayView
都是
ContentView
的子級,
它們會自動繼承其環(huán)境。
提示:鑒于我們明確地與其他視圖共享我們的實例User
,我個人傾向于刪除private
訪問控制,因為它不準確。
現(xiàn)在,你可能想知道 SwiftUI 如何在.environmentObject(user)和
?@EnvironmentObject var user: User
之間建立聯(lián)系
——它如何知道將該對象放入正確的屬性中?
嗯,你已經(jīng)看到字典如何讓我們使用一種類型作為鍵,另一種類型作為值。該環(huán)境有效地讓我們將數(shù)據(jù)類型本身用作鍵,并將類型的實例用作值。一開始這有點讓人費解,但可以這樣想象:鍵是像Int
、
String
和
Bool
之類的東西
,值是 5、“你好” 和 true 之類的東西,這意味著我們可以說“給我Int
”和我們會回來5。



使用 TabView 和 tabItem() 創(chuàng)建選項卡
導(dǎo)航視圖非常適合讓我們創(chuàng)建層次化的視圖堆棧,讓用戶深入了解數(shù)據(jù),但它們不能很好地顯示不相關(guān)的數(shù)據(jù)。為此,我們需要使用 SwiftUI 的TabView
,它會在屏幕底部創(chuàng)建一個按鈕條,點擊每個按鈕會顯示不同的視圖。
將選項卡放在一個TabView
中
就像將它們一一列出一樣簡單,如下所示:
然而,在實踐中,你總是希望自定義選項卡的顯示方式——在上面的代碼中,選項卡欄將是一個空白的灰色區(qū)域。盡管你可以點擊該灰色區(qū)域的左右部分來激活這兩個選項卡,但這是一種非常糟糕的用戶體驗。
相反,最好將修飾符附加tabItem()
到TabView
.?這使你可以自定義視圖在選項卡欄中的顯示方式,提供圖像和一些文本顯示在它旁邊,如下所示:
除了讓用戶通過點擊選項卡項來切換視圖外,SwiftUI 還允許我們使用狀態(tài)以編程方式控制當前視圖。這需要四個步驟:
創(chuàng)建一個
@State
屬性來跟蹤當前顯示的選項卡。每當我們想跳轉(zhuǎn)到不同的選項卡時,將該屬性修改為新值。
將它作為綁定傳遞到
TabView
中
,因此它會被自動跟蹤。告訴 SwiftUI 應(yīng)該為該屬性的每個值顯示哪個選項卡。
其中前三個很簡單,所以讓我們把它們移開。首先,我們需要一些狀態(tài)來跟蹤當前選項卡,因此將其作為屬性添加到ContentView
:
其次,我們需要在某處修改它,這將要求 SwiftUI 切換選項卡。在我們的小演示中,我們可以將onTapGesture()
修飾符附加到第一個選項卡,如下所示:
第三,我們需要綁定到的TabView
選擇$selectedTab
。這是在我們創(chuàng)建 TabView
時作為參數(shù)傳遞的
,因此請將你的代碼更新為:
現(xiàn)在是有趣的部分:當我們說selectedTab = "Two"?
SwiftUI 如何知道代表哪個選項卡時?你可能認為可以將選項卡視為一個數(shù)組,在這種情況下,第二個選項卡將位于索引 1 處,但這會導(dǎo)致各種問題:如果我們將該選項卡移動到選項卡視圖中的不同位置會怎樣?
在更深層次上,它還打破了 SwiftUI 的核心概念之一:我們應(yīng)該能夠自由地組合視圖。如果選項卡 1 是數(shù)組中的第二項,則:
Tab 0 是第一個選項卡。
選項卡 1 是第二個選項卡。
標簽 0 有一個
onTapGesture()
顯示標簽 1 的標簽。因此,選項卡 0 非常了解其父項
TabView
的
配置方式。
這是一個非常糟糕的想法,因此 SwiftUI 提供了一個更好的解決方案:我們可以為每個視圖附加一個唯一標識符,并將其用于選定的選項卡。這些標識符稱為標簽,并使用tag()
如下修飾符附加:
所以,我們的整體觀點是這樣的:
現(xiàn)在代碼可以工作了:你可以通過按下選項卡項或在第一個選項卡中激活我們的點擊手勢來在選項卡之間切換。
當然,只使用“One”和“Two”并不理想——這些值是固定的,因此它解決了視圖移動的問題,但它們不容易記住。幸運的是,你可以改用任何你喜歡的值:為每個視圖提供一個唯一且反映其用途的字符串標記,然后將其用于你的@State
屬性。從長遠來看,這更容易使用,并且推薦使用整數(shù)。
提示:想同時使用NavigationView
和TabView
是很常見的,但你要小心:
TabView
應(yīng)該是父視圖,里面的選項卡根據(jù)需要有一個
NavigationView
,而不能反過來。


