SwiftUI學(xué)習(xí)100天(Day81 - 項(xiàng)目 16,第三部分)

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

今天我們將研究三個(gè)重要功能:SwiftUI 中的上下文菜單、iOS 中的通知和 Xcode 中的 Swift 包依賴項(xiàng)。這是應(yīng)用程序開發(fā)人員需要掌握的另外三個(gè)真正關(guān)鍵的功能,我希望你會(huì)發(fā)現(xiàn)所有這些功能都相對(duì)容易學(xué)習(xí)。
上下文菜單——當(dāng)你在 iOS 中按住某些東西時(shí)彈出的菜單——允許我們?yōu)椴季种械囊晥D添加額外的操作。這是避免給你的 UI 添加混亂的好方法,但我希望你在這樣做時(shí)要謹(jǐn)慎行事。Scott Belsky 曾是 Adobe 的副總裁,現(xiàn)在是風(fēng)險(xiǎn)投資家,他曾經(jīng)說過,設(shè)計(jì)良好的用戶體驗(yàn)有一條規(guī)則:“更多選擇,更多問題。”
因此,一定要為你的 UI 添加額外的功能,但始終要考慮可發(fā)現(xiàn)性。畢竟,如果用戶找不到你的操作,那么它們可能就不存在了!
今天,你需要完成三個(gè)主題,你將在其中了解上下文菜單、本地通知、Swift 包依賴項(xiàng)等。

創(chuàng)建上下文菜單
當(dāng)用戶點(diǎn)擊按鈕或?qū)Ш芥溄訒r(shí),很明顯 SwiftUI 應(yīng)該觸發(fā)這些視圖的默認(rèn)操作。但是,如果他們按住某物怎么辦?在舊版 iPhone 上,用戶可以通過用力按壓某物來觸發(fā) 3D Touch,但原理是一樣的:用戶想要與他們交互的任何東西都有更多選擇。
SwiftUI 允許我們將上下文菜單附加到對(duì)象以提供這種額外的功能,所有這些都是使用contextMenu()
修飾符完成的。你可以將選擇的按鈕傳遞給它,它們將按順序顯示,因此我們可以構(gòu)建一個(gè)簡單的上下文菜單來控制視圖的背景顏色,如下所示:
就像 TabView
一樣
,上下文菜單中的每個(gè)項(xiàng)目都可以使用Label
視圖附加文本和圖像。
現(xiàn)在,這里有一個(gè)問題:為了讓用戶界面在應(yīng)用程序中看起來有些統(tǒng)一,iOS 將每個(gè)圖像呈現(xiàn)為純色,其中保留了不透明度。這使得許多圖片變得毫無用處:如果你有三張不同狗的三張照片,所有三張圖片都會(huì)呈現(xiàn)為純黑色方塊,因?yàn)樗蓄伾急灰瞥恕?/p>
相反,你應(yīng)該瞄準(zhǔn)線條藝術(shù)圖標(biāo),例如 Apple 的 SF Symbols,如下所示:
當(dāng)你運(yùn)行它時(shí),你會(huì)看到foregroundColor()
修改器被忽略了——iOS 確實(shí)希望我們的菜單看起來統(tǒng)一,所以試圖隨機(jī)給它們上色是行不通的。如果你真的希望該項(xiàng)目顯示為紅色,你應(yīng)該知道這意味著破壞性,你應(yīng)該改用按鈕角色:
在使用上下文菜單時(shí),我為你提供了一些提示,以幫助確保你為用戶提供最佳體驗(yàn):
如果你要使用它們,請(qǐng)?jiān)诤芏嗟胤绞褂盟鼈儭绻醋∧硞€(gè)東西卻發(fā)現(xiàn)什么也沒有發(fā)生,這可能會(huì)令人沮喪。
使你的選項(xiàng)列表盡可能短——目標(biāo)是三個(gè)或更少。
不要重復(fù)用戶已經(jīng)可以在 UI 的其他地方看到的選項(xiàng)。
請(qǐng)記住,上下文菜單本質(zhì)上是隱藏的,因此在將重要操作隱藏在上下文菜單中之前請(qǐng)三思。



將自定義行滑動(dòng)操作添加到列表
從我記事起,iOS 應(yīng)用程序就具有“滑動(dòng)刪除”功能,但近年來它們的功能越來越強(qiáng)大,因此列表行可以有多個(gè)按鈕,通常位于行的兩側(cè)。我們使用修飾符在 SwiftUI 中獲得了這個(gè)完整的功能swipeActions()
,它允許我們?cè)诹斜硇械囊粋?cè)或兩側(cè)注冊(cè)一個(gè)或多個(gè)按鈕。
默認(rèn)情況下按鈕將放置在行的右邊緣,并且不會(huì)有任何顏色,因此當(dāng)你從右向左滑動(dòng)時(shí)將顯示一個(gè)灰色按鈕:
你可以通過向修改器
swipeActions()
,并且可以通過向按鈕添加提供edge
參數(shù)來自定義放置按鈕的邊緣tint()
,
你選擇的顏色的修改器或附加按鈕角色來自定義按鈕的顏色。
因此,這將在我們行的兩側(cè)顯示一個(gè)按鈕:
與上下文菜單一樣,滑動(dòng)操作本質(zhì)上默認(rèn)對(duì)用戶是隱藏的,因此不要在其中隱藏重要功能很重要。我們將在此應(yīng)用程序中同時(shí)使用它們,希望你有機(jī)會(huì)直接比較和對(duì)比它們!



安排本地通知
iOS 有一個(gè)名為 UserNotifications 的框架,它幾乎完全符合你的期望:讓我們?yōu)橛脩魟?chuàng)建可以在鎖定屏幕上顯示的通知。我們有兩種類型的通知可以使用,它們根據(jù)創(chuàng)建位置的不同而有所不同:本地通知是我們?cè)诒镜匕才诺耐ㄖ?,遠(yuǎn)程通知(通常稱為推送通知)是從某處服務(wù)器發(fā)送的。
遠(yuǎn)程通知需要服務(wù)器才能工作,因?yàn)槟銓⑾l(fā)送到 Apple 的推送通知服務(wù) (APNS),然后由后者將其轉(zhuǎn)發(fā)給用戶。但是相比之下,本地通知要好得多,也容易一些,因?yàn)橹灰脩粼试S,我們可以隨時(shí)發(fā)送任何消息。
要嘗試這個(gè),首先在 ContentView.swift 的頂部附近添加一個(gè)額外的導(dǎo)入:
接下來我們將放入一些我們將用本地通知代碼填充的基本結(jié)構(gòu)。使用本地通知需要請(qǐng)求用戶許可,然后實(shí)際注冊(cè)我們要顯示的通知。我們會(huì)將這些操作中的每一個(gè)放入一個(gè)VStack
內(nèi)的單獨(dú)按鈕中
,因此請(qǐng)現(xiàn)在將其放入你的ContentView
結(jié)構(gòu)體中:
好的,我們的設(shè)置已經(jīng)完成,所以讓我們將注意力轉(zhuǎn)移到兩個(gè)重要工作中的第一個(gè):請(qǐng)求授權(quán)以顯示警報(bào)。通知可以采用多種形式,但最常見的做法是請(qǐng)求顯示警報(bào)、徽章和聲音的許可——這并不意味著我們需要同時(shí)使用所有這些,而是預(yù)先請(qǐng)求許可意味著我們以后可以有選擇。
當(dāng)我們告訴 iOS 我們想要什么樣的通知時(shí),它會(huì)向用戶顯示一個(gè)提示,這樣他們對(duì)我們的應(yīng)用程序可以做什么有最終決定權(quán)。當(dāng)他們做出選擇時(shí),我們提供的閉包將被調(diào)用并告訴我們請(qǐng)求是否成功。
所以,用這個(gè)替換評(píng)論// first
:
如果用戶授予權(quán)限,那么我們就可以開始安排通知了。盡管通知可能看起來很簡單,但 Apple 將它們分為三個(gè)部分以提供最大的靈活性:
內(nèi)容是應(yīng)該顯示的內(nèi)容,可以是標(biāo)題、副標(biāo)題、聲音、圖像等。
觸發(fā)器確定何時(shí)應(yīng)顯示通知,可以是從現(xiàn)在開始的秒數(shù)、未來的日期和時(shí)間或位置。
該請(qǐng)求結(jié)合了內(nèi)容和觸發(fā)器,還添加了一個(gè)唯一標(biāo)識(shí)符,以便你稍后可以編輯或刪除特定警報(bào)。如果你不想編輯或刪除內(nèi)容,請(qǐng)使用
UUID().uuidString
獲取隨機(jī)標(biāo)識(shí)符。
當(dāng)你剛開始學(xué)習(xí)通知時(shí),最容易使用的觸發(fā)器類型是UNTimeIntervalNotificationTrigger
,它允許我們請(qǐng)求在從現(xiàn)在起的特定秒數(shù)后顯示通知。所以,用這個(gè)替換評(píng)論// second
:
如果你現(xiàn)在運(yùn)行呢的應(yīng)用程序,請(qǐng)按第一個(gè)按鈕請(qǐng)求通知權(quán)限,然后按第二個(gè)按鈕添加實(shí)際通知。
現(xiàn)在是重要的部分:添加通知后,在模擬器中按 Cmd+L 鎖定屏幕。幾秒鐘后,設(shè)備應(yīng)該會(huì)被聲音喚醒,并顯示我們的消息——很好!



在 Xcode 中添加 Swift 包依賴
到目前為止我們編寫的所有代碼都是我們從頭開始構(gòu)建的東西,因此你可以確切地看到它是如何工作的并將這些技能應(yīng)用到你自己的項(xiàng)目中。但有時(shí),從頭開始編寫一些東西是有風(fēng)險(xiǎn)的:也許代碼很復(fù)雜,也許很容易出錯(cuò),也許它經(jīng)常更改,或者任何其他無數(shù)原因,這就是存在依賴關(guān)系的原因——獲取第三方代碼的能力并在我們的項(xiàng)目中使用它。
Xcode 內(nèi)置了一個(gè)依賴管理器,稱為 Swift Package Manager (SPM)。你可以告訴 Xcode 一些在線存儲(chǔ)的代碼的 URL,它會(huì)為你下載。你甚至可以告訴它要下載哪個(gè)版本,這意味著如果遠(yuǎn)程代碼在將來某個(gè)時(shí)候發(fā)生更改,你可以確保它不會(huì)破壞你現(xiàn)有的代碼。
為了嘗試這一點(diǎn),我創(chuàng)建了一個(gè)簡單的 Swift 包,你可以將其導(dǎo)入到任何項(xiàng)目中。這為 Swift 的Sequence
類型(Array
、Set
、Dictionary
甚至范圍都符合)添加了一個(gè)小的擴(kuò)展,可以同時(shí)抽取一些隨機(jī)項(xiàng)。
不管怎樣,第一步是將包添加到我們的項(xiàng)目中:轉(zhuǎn)到“文件”菜單并選擇“添加包”。對(duì)于 URL,請(qǐng)輸入https://github.com/twostraws/SamplePackage,這是我的示例包的代碼存儲(chǔ)位置。Xcode 將獲取包,讀取其配置,并向你顯示選項(xiàng),詢問你要使用哪個(gè)版本。默認(rèn)值為“Version – Up to Next Major”,這是最常用的版本,意味著如果包的作者將來更新它,那么只要他們不引入重大更改,Xcode 就會(huì)更新包使用新版本。
這是可能的原因是因?yàn)榇蠖鄶?shù)開發(fā)人員已經(jīng)同意為其代碼使用語義版本控制系統(tǒng) (SemVer)。如果你查看像 1.5.3 這樣的版本,那么 1 被認(rèn)為是主要編號(hào),5 被認(rèn)為是次要編號(hào),而 3 被認(rèn)為是補(bǔ)丁編號(hào)。如果開發(fā)人員正確遵循 SemVer,那么他們應(yīng)該:
只要不破壞任何 API 或添加功能,在修復(fù)錯(cuò)誤時(shí)更改補(bǔ)丁號(hào)。
當(dāng)他們添加不破壞任何 API 的功能時(shí)更改次要編號(hào)。
當(dāng)他們破壞 API時(shí)更改主編號(hào)。
這就是“Up to Next Major”如此有效的原因,因?yàn)樗鼞?yīng)該意味著隨著時(shí)間的推移你會(huì)獲得新的錯(cuò)誤修復(fù)和功能,但不會(huì)意外切換到破壞你代碼的版本。
無論如何,我們已經(jīng)完成了我們的包,所以單擊 Finish 讓 Xcode 將它添加到項(xiàng)目中。你應(yīng)該會(huì)看到它出現(xiàn)在項(xiàng)目導(dǎo)航器中的“Swift Package Dependencies”下。
要嘗試一下,請(qǐng)打開 ContentView.swift 并將此導(dǎo)入添加到頂部:
是的,這個(gè)外部依賴現(xiàn)在是一個(gè)模塊,我們可以在任何需要的地方導(dǎo)入它。
現(xiàn)在我們可以在我們的視圖中嘗試它。例如,我們可以通過制作從 1 到 60 的數(shù)字范圍來模擬一個(gè)簡單的彩票,從中挑選 7 個(gè),將它們轉(zhuǎn)換為字符串,然后將它們連接成一個(gè)字符串。簡而言之,這將需要一些你以前從未見過的代碼,因此我將對(duì)其進(jìn)行分解。
首先,用這個(gè)替換你的ContentView
:
是的,那是行不通的,因?yàn)?span id="s0sssss00s" class="color-pink-02">results
不見了
,但我們現(xiàn)在要填補(bǔ)它。
首先,可以通過將此屬性添加到 ContentView
來創(chuàng)建從 1?到 60 的數(shù)字范圍
:
其次,我們將創(chuàng)建一個(gè)名為results
的計(jì)算屬性
,它從那里挑選七個(gè)數(shù)字并將它們變成一個(gè)字符串,因此也添加這個(gè)屬性:
在里面,我們將從我們的范圍中選擇七個(gè)隨機(jī)數(shù),這可以使用你從我的 SamplePackage 框架中獲得的擴(kuò)展來完成。這提供了一種random()
方法,該方法接受一個(gè)整數(shù)并將以隨機(jī)順序從你的序列中返回最多該數(shù)量的隨機(jī)元素。彩票號(hào)碼通常是從小到大排列的,所以我們要對(duì)它們進(jìn)行排序。
因此,添加這行代碼代替// more code to come
:
接下來,我們需要將該整數(shù)數(shù)組轉(zhuǎn)換為字符串。這在 Swift 中只需要一行代碼,因?yàn)樾蛄杏幸粋€(gè)map()
方法可以讓我們通過對(duì)每個(gè)元素應(yīng)用一個(gè)函數(shù)將一種類型的數(shù)組轉(zhuǎn)換為另一種類型的數(shù)組。在我們的例子中,我們想從每個(gè)整數(shù)初始化一個(gè)新的字符串,這樣我們就可以用作String.init
我們想要調(diào)用的函數(shù)。
因此,在上一行之后添加這一行:
此時(shí)strings
是一個(gè)字符串?dāng)?shù)組,其中包含我們范圍內(nèi)的七個(gè)隨機(jī)數(shù),因此最后一步是將它們?nèi)窟B接在一起,中間用逗號(hào)分隔?,F(xiàn)在將最后一行添加到屬性中:
這就完成了我們的代碼:文本視圖將顯示里面的值results
,它將繼續(xù)選擇隨機(jī)數(shù),對(duì)它們進(jìn)行排序,將它們字符串化,然后用逗號(hào)連接它們。
PS:你可以直接在 Xcode 中閱讀我的簡單擴(kuò)展的源代碼——只需打開 Sources > SamplePackage 組并查找 SamplePackage.swift。你會(huì)發(fā)現(xiàn)它并沒有多大作用!
這完成了該項(xiàng)目所需的最終技術(shù),因此請(qǐng)將你的代碼重置為原始狀態(tài)。


