SwiftUI:跨語(yǔ)言、跨平臺(tái)、小組件

原創(chuàng):知識(shí)點(diǎn)總結(jié)性文章
創(chuàng)作不易,請(qǐng)珍惜,之后會(huì)持續(xù)更新,不斷完善
個(gè)人比較喜歡做筆記和寫總結(jié),畢竟好記性不如爛筆頭哈哈,這些文章記錄了我的IOS成長(zhǎng)歷程,希望能與大家一起進(jìn)步
溫馨提示:由于簡(jiǎn)書不支持目錄跳轉(zhuǎn),大家可通過command + F 輸入目錄標(biāo)題后迅速尋找到你所需要的內(nèi)容

目錄

  • 一、跨國(guó)應(yīng)用本地化
    • 1、商店描述
    • 2、UI 界面
    • 3、應(yīng)用名稱
    • 4、交給別人翻譯
  • 二、跨平臺(tái)應(yīng)用
    • 1、創(chuàng)建跨平臺(tái)應(yīng)用
    • 2、Mac 應(yīng)用版本間的差異
    • 3、區(qū)分 iPhone 及 Mac 平臺(tái)
    • 4、區(qū)分 iPadOS 及 iOS 平臺(tái)
    • 5、Apple Watch 應(yīng)用
    • 6、Apple TV 應(yīng)用
    • 7、判斷 UI 在各平臺(tái)上的可用性
  • 三、桌面小組件 Widgets
    • 1、背景
    • 2、系統(tǒng)級(jí)別的功能
    • 3、制作桌面小組件的流程
    • 4、代碼詳解

一、跨國(guó)應(yīng)用本地化

應(yīng)用本地化 Localization,主要指的是將現(xiàn)有應(yīng)用,根據(jù)不同地區(qū)的語(yǔ)言習(xí)慣翻譯為不同語(yǔ)種。除此之外,它還意味著對(duì)當(dāng)?shù)氐氖褂昧?xí)慣進(jìn)行針對(duì)性調(diào)整,比如閱讀語(yǔ)序等。SwiftUI 已經(jīng)自動(dòng)處理好了諸如文本框中的使用方向,導(dǎo)航器的呼出方向等問題,因此獨(dú)立開發(fā)者主要需要做的本例化工作便是翻譯。

在設(shè)計(jì)一款目標(biāo)發(fā)售地包含國(guó)際市場(chǎng)的應(yīng)用時(shí),你需要?jiǎng)?wù)必留意所有字符串的長(zhǎng)度。中文作為一種用方塊文字表達(dá)意思的語(yǔ)言,其簡(jiǎn)短的詞組便可以表達(dá)完整意思。而世界上很多語(yǔ)言是拉丁語(yǔ)系的,主要是用詞根和詞綴來對(duì)單詞進(jìn)行修飾,甚至在不少語(yǔ)言中物品還存在男女的叫法,因此描述起來非常長(zhǎng)。比如某個(gè)單詞在中文中只需要兩個(gè)字符的位置,在德語(yǔ)中可能需要十幾個(gè)字符位。

考慮到語(yǔ)言上的差異,在設(shè)計(jì)應(yīng)用程序界面時(shí)應(yīng)該留足空位,以便讓不同語(yǔ)言能完整顯示出來。對(duì)于界面元素,能用 SwiftUI 的就用,盡量避免直接從設(shè)計(jì)軟件中直接導(dǎo)入帶文字內(nèi)容的界面元素,這樣會(huì)給日后的本地化工作平添麻煩。

應(yīng)用程序的核心邏輯常不受語(yǔ)言限制,將應(yīng)用翻譯為不同語(yǔ)言,十分有助于擴(kuò)展銷路。通常來說,歐洲、美國(guó)、中國(guó)這三大市場(chǎng)競(jìng)爭(zhēng)較為激烈,想要讓應(yīng)用獲得較好的排位相對(duì)困難。但競(jìng)爭(zhēng)激烈的同時(shí),這些市場(chǎng)的回報(bào)也最高,值得為其所在地的語(yǔ)言進(jìn)行應(yīng)用翻譯工作。

對(duì)于較為小眾的市場(chǎng),諸如非洲或南亞各國(guó),通常消費(fèi)力更低一些。Apple 本身的產(chǎn)品滲透率在這些地區(qū)也稍差,這時(shí)候能做到多少,要做多少就得由你自己進(jìn)行取舍了。但有一點(diǎn)是確定的,市場(chǎng)再小也是市場(chǎng),且這些小眾市場(chǎng)往往競(jìng)爭(zhēng)相對(duì)緩和,若你能對(duì)他們的語(yǔ)言進(jìn)行針對(duì)性優(yōu)化,成功的可能性反而更大些。

本文將以支持「英文、中文」的應(yīng)用為例,介紹應(yīng)用本地化工作的方法。

1、商店描述

應(yīng)用程序在 App Store 中的全部描述由 App Store Connect 網(wǎng)站負(fù)責(zé)。如下圖所示,當(dāng)你希望添加不同語(yǔ)種的應(yīng)用簡(jiǎn)介、更新公告時(shí),可以點(diǎn)擊下圖右側(cè)的語(yǔ)言按鈕。選中語(yǔ)言過后,點(diǎn)擊右側(cè)的「+」按鈕來新增應(yīng)用描述所支持的語(yǔ)言。

部分人只會(huì)翻譯應(yīng)用描述,卻忽略了預(yù)覽圖部分。若你在對(duì)主要的市場(chǎng)進(jìn)行翻譯工作時(shí),強(qiáng)烈建議一并更新預(yù)覽圖。假設(shè)不對(duì)預(yù)覽圖進(jìn)行單獨(dú)修改,則 App Store 會(huì)默認(rèn)使用默認(rèn)語(yǔ)言,如英文的預(yù)覽圖在全球的商店顯示。

若你需要對(duì)預(yù)覽截圖進(jìn)行本地化,可以長(zhǎng)按應(yīng)用程序圖標(biāo),并選擇「Edit Scheme」。在彈出的下列界面中,找到「App Language 應(yīng)用語(yǔ)言」,將其修改為待截圖的目標(biāo)語(yǔ)言即可。

在 App Store Connect 的語(yǔ)言列表中,你會(huì)看到某個(gè)語(yǔ)言帶有「Primary」標(biāo)識(shí),如下圖所示。在當(dāng)前應(yīng)用中,Primary 語(yǔ)言指的是在開發(fā)者沒有提供對(duì)應(yīng)內(nèi)容的本地化翻譯時(shí),任何素材、簡(jiǎn)介、截圖等信息的默認(rèn)顯示語(yǔ)言。

應(yīng)用名稱、標(biāo)題的翻譯在 App Store Connect 的「App Information 應(yīng)用信息」分區(qū)中,如下圖所示。點(diǎn)擊右側(cè)的語(yǔ)言按鈕,可以在不同語(yǔ)言中切換。在上文中已添加中文,因此下圖中也已經(jīng)包含了中文選項(xiàng)。切換至不同語(yǔ)言后,將翻譯填入下圖表格中即可。


2、UI 界面

應(yīng)用啟動(dòng)后,顯示出的任何文字都可以說是 UI 界面的一部分,翻譯這一部分內(nèi)容至關(guān)重要。在 Xcode 中依次選中「項(xiàng)目名 - Project 標(biāo)簽頁(yè)」,如下圖所示。下圖中包含一個(gè) Localization 的選項(xiàng),此處默認(rèn)情況下只有一個(gè)叫做 English - Development Language 的選項(xiàng),你可以把英文當(dāng)作代碼層面的默認(rèn)設(shè)置。想要添加新的語(yǔ)言支持,點(diǎn)擊「+」即可,本例中我選擇添加了簡(jiǎn)體中文 Simplified Chinese

Xcode 對(duì) SwiftUI 中 UI 本地化流程有點(diǎn)像是查詢電話號(hào)碼本。該電話號(hào)碼本的文件類型為 Strings File。如下圖所示,新建一個(gè) Strings File。

將該文件命名為 Localizable.strings,代表 UI 翻譯文件,之后點(diǎn)擊「Create」創(chuàng)建。

創(chuàng)建完成后,Xcode 會(huì)提供默認(rèn)英文版的電話號(hào)碼本,如下圖所示。但我們還額外需要中文的電話號(hào)碼本,因此點(diǎn)擊下圖中右側(cè)的「Localize 本地化」按鈕。

點(diǎn)擊 Localize 后,在下圖右側(cè)彈出的本地化選項(xiàng)中勾選英文和中文兩種語(yǔ)言。勾選完成后,Xcode 會(huì)自動(dòng)在左側(cè)創(chuàng)建兩個(gè)號(hào)碼本,一個(gè)是英文版,一個(gè)是中文版,如下圖所示。

在 SwiftUI 中,所有文本框中輸入的字符,都會(huì)被 Xcode 視作是在號(hào)碼本中查詢的鑰匙。比如下圖的簡(jiǎn)單范例中,UI 是一個(gè)純文本視圖,顯示「Three Good Things」,此時(shí) Three Good Things 便會(huì)被 Xcode 視作鑰匙拿去號(hào)碼本中查詢,如果號(hào)碼本中存在完全匹配的字符串,則直接用號(hào)碼本中的翻譯,如果沒有,則直接顯示 Three Good Things 這段未經(jīng)翻譯的文字。

想要讓 Xcode 知道這段話該如何翻譯,你需要依次進(jìn)入剛才創(chuàng)建好的中文和英文電話號(hào)碼本,依次輸入 Three Good Things 的翻譯方式,如下圖所示,在中文的號(hào)碼本中 Three Good Things 會(huì)被翻譯為三件好事,英文的號(hào)碼本中會(huì)被翻譯為 Three Good Things 保持不變。值得注意的是,以中文翻譯「"Three Good Things" = "三件好事";」為例,等號(hào)左側(cè)的鑰匙必須要與 SwiftUI 中輸入的字符串完全匹配,等號(hào)右側(cè)的則是對(duì)應(yīng)的本地化翻譯,結(jié)束時(shí)必須用到英文分號(hào)「;」。

翻譯完成后,分別運(yùn)行中文版和英文版的虛擬機(jī),UI 效果如下。至此應(yīng)用的 UI 界面完成本地化翻譯。


3、應(yīng)用名稱

應(yīng)用名稱的翻譯與界面翻譯非常類似,不過需要?jiǎng)?chuàng)建一個(gè)專門用于查詢應(yīng)用顯示名稱的號(hào)碼本。如下圖所示,新建一個(gè) Strings File。

將該文件命名為「InfoPlist.strings」并創(chuàng)建文件。

與上文類似,點(diǎn)擊 Localize 按鈕將 Info.Plist 文件分別創(chuàng)建為兩種語(yǔ)言的號(hào)碼本。

打開對(duì)應(yīng)的語(yǔ)言文件并進(jìn)行翻譯,比如「"CFBundleDisplayName" = "三件好事";」會(huì)將該應(yīng)用的中文顯示名稱改為「三件好事」。本例中,CFBundleDisplayName 指的是 Xcode 用于自動(dòng)識(shí)別應(yīng)用名稱的鑰匙。

若你使用上述虛擬機(jī)切換語(yǔ)言的方法,會(huì)發(fā)現(xiàn)應(yīng)用的顯示名本地化沒有正確顯示。這是因?yàn)樵?iOS 設(shè)備時(shí),所有應(yīng)用名稱不跟隨應(yīng)用自身設(shè)置,而是全部跟隨系統(tǒng)顯示,因此想要查看翻譯是否成功,需要在虛擬機(jī)的設(shè)置中,切換系統(tǒng)語(yǔ)言,如下圖中間的截圖所示,之后本地化應(yīng)用名稱即可正常顯示。


4、交給別人翻譯

對(duì)于機(jī)器翻譯來說,我個(gè)人比較推薦 DeepL,它的翻譯在水準(zhǔn)之上,且支持語(yǔ)言較多。筆者之前寫過一篇 介紹 DeepL 用法及效果的文章,不再贅述。但還有些情況,你可能會(huì)拜托朋友第三方翻譯幫你進(jìn)行本地化工作,這時(shí)便需要把文件交給別人,這時(shí)便需要分享源文件。打開 Xcode,并右鍵選擇「Show in Finder」。

在打開的 Xcode 項(xiàng)目中,你會(huì)看到尾綴是 .lproj 的本地化翻譯文件。比如 en 指的就是英文翻譯,zh-Hans 指的是中文翻譯。假設(shè)你希望請(qǐng)別人翻譯英文版,只需要把 en.lproj 文件夾分享給他人即可,不需要分享整個(gè) Xcode 項(xiàng)目文件。

以中文為例,本地化文件夾中包含 InfoPlist.strings 應(yīng)用名稱文件,以及 Localizable.strings 應(yīng)用 UI 文件。若對(duì)方的機(jī)器是 Windows,或沒有 Xcode 的 Mac,你可以請(qǐng)對(duì)方用任意的文本編輯器打開進(jìn)行翻譯。以 Mac 為例,右鍵打開該文件,選擇用自帶的文本編輯器「TextEdit」打開即可。

打開后的文件如下圖所示,修改好翻譯之后保存。當(dāng)開發(fā)者拿到翻譯完的文件夾后,直接粘貼到 Xcode 項(xiàng)目中覆蓋舊的翻譯文件夾即可。


二、跨平臺(tái)應(yīng)用

隨著 Mac 產(chǎn)品線逐漸過渡到 M 系列芯片,SwiftUI 轉(zhuǎn)變成為 Apple 生態(tài)系統(tǒng)中全部平臺(tái)的主要 UI 語(yǔ)言,跨平臺(tái)應(yīng)用開發(fā)變得前所未有地簡(jiǎn)單。對(duì)于開發(fā)者來說,使用 SwiftUI 的標(biāo)準(zhǔn)控件,可以讓你無需操心如何定制適用于各個(gè)平臺(tái)的 UI,只需要用一套寫法,在每個(gè)系統(tǒng)平臺(tái)中都會(huì)自動(dòng)適配相應(yīng)的 UI 外觀。

1、創(chuàng)建跨平臺(tái)應(yīng)用

若你只需要某個(gè)平臺(tái)的應(yīng)用,可以直接選擇對(duì)應(yīng)的「iOS」或「macOS」應(yīng)用模版進(jìn)行創(chuàng)建。若你想創(chuàng)建跨 iPad、iOS、macOS 三平臺(tái)應(yīng)用,只需要在 Xcode 創(chuàng)建應(yīng)用的模版中選擇「Multiplatform - App」即可,如下圖所示。

跨三平臺(tái)的應(yīng)用默認(rèn)使用 SwiftUI 作為 UI 界面語(yǔ)言,使用 SwiftUI Lifecycle 用作生命周期控制,無任何使用其它選擇,如下圖所示。


2、Mac 應(yīng)用版本間的差異

未來數(shù)年中,會(huì)出現(xiàn)英特爾芯片與 M 系列 Mac 機(jī)型并存的狀態(tài),因此 Mac 版本和運(yùn)作方式稍顯復(fù)雜。創(chuàng)建完跨平臺(tái)應(yīng)用后,你會(huì)注意到應(yīng)用運(yùn)行窗口有兩個(gè)選項(xiàng),其中之一備注為 iOS,其二備注為 macOS。

這里有個(gè)選項(xiàng)你也許會(huì)覺得奇怪,在 iOS 大類中,居然出現(xiàn)了 My Mac (Designed for iPad) 選項(xiàng)。這個(gè)選項(xiàng)指的是該應(yīng)用仍然為 iOS 應(yīng)用,若開發(fā)者不進(jìn)行額外說明,默認(rèn)支持所有搭載 M 芯片的 Mac 電腦運(yùn)行。本質(zhì)上來說,以該模式運(yùn)行的應(yīng)用仍然為原版的 iOS 應(yīng)用。在此模式下,所有 UI 顯示方式和操作手勢(shì)等完全與 iOS 版本相同。

在 macOS 應(yīng)用的選項(xiàng)中,出現(xiàn)了兩個(gè) Mac 對(duì)應(yīng)類型,分別是 My MacMy Mac (Rosetta),均指的是以 Mac 版本的 UI 進(jìn)行顯示。若你正在使用的 Mac 開發(fā)機(jī)器搭載 M1 芯片,則 My Mac 指的是讓應(yīng)用以 x86 原生模式運(yùn)行;若你正在使用的 Mac 開發(fā)機(jī)器搭載 M1 芯片,則 My Mac 指的是讓應(yīng)用以 ARM 原生模式運(yùn)行。在 M1 版本的機(jī)器上,還會(huì)額外出現(xiàn) Rosetta 的選項(xiàng),指的是應(yīng)用以 x86 形式編譯,通過兼容層在 M 芯片的機(jī)器上運(yùn)行。

若你在應(yīng)用中用到了較老的 UIKit 作為視圖框架,還可能在運(yùn)行欄中看到 My Mac (Catalyst),指的是將 UIKit 應(yīng)用以 MacCatalyst 翻譯后,運(yùn)行在 Mac 機(jī)型中。請(qǐng)注意,以上所有的選項(xiàng),均取決于你的開發(fā)機(jī)器,可能和本文截圖有所差異??偟膩碚f,現(xiàn)階段下 Mac 應(yīng)用運(yùn)行共有 6 種方式,分別是:

  • Mac (Designed for iPad):純 iOS 應(yīng)用,僅支持 M 芯片版 Mac
  • Mac (Intel):純 macOS 應(yīng)用,僅支持 Intel 芯片版 Mac
  • Mac (ARM):純 macOS 應(yīng)用,僅支持 M 芯片版 Mac
  • Mac (Rosetta):純 macOS 應(yīng)用,支持 M 及 Intel 芯片版 Mac
  • Mac (Catalyst):較老的寫法。純 Mac 版 UIKit 應(yīng)用,支持 M 及 Intel 芯片版 Mac
  • Mac (Appkit):較老的寫法。純 Appkit 應(yīng)用,支持 M 及 Intel 芯片版 Mac

3、區(qū)分 iPhone 及 Mac 平臺(tái)

你也許會(huì)問,同樣的代碼,假設(shè)我需要?jiǎng)?chuàng)建出兩種不同的視圖排版怎么辦。遇到特別復(fù)雜的界面差異,你可以考慮新建一個(gè) SwiftUI 視圖,來單獨(dú)處理某個(gè)平臺(tái)的顯示。若你只需要進(jìn)行一些細(xì)微變動(dòng),則可以使用語(yǔ)法 #if os(xxx) #endif 來進(jìn)行針對(duì)性判斷。如下圖所示,如果軟件運(yùn)行在 iOS 設(shè)備上,則顯示 iOS 文字,反之則顯示 macOS 文字。

將包含上述代碼所示的應(yīng)用運(yùn)行后,結(jié)果如下??梢钥吹?,雖然是同一個(gè) SwiftUI 視圖,內(nèi)容根據(jù)平臺(tái)進(jìn)行了差異化顯示。


4、區(qū)分 iPadOS 及 iOS 平臺(tái)

Apple 當(dāng)下的生態(tài)系統(tǒng)命名有些不規(guī)范,雖然 iPad 上設(shè)備名義上運(yùn)行的是 iPadOS 操作系統(tǒng),但是在 Xcode 體系中,仍然被歸類為 iOS。因此上述判斷方案無法得知應(yīng)用是運(yùn)行在 iPhone 還是 iPad 上。此時(shí)可以用到另一種判斷方法,如果 UIDevice.current.userInterfaceIdiom == .pad 為真,則運(yùn)行在 iPad 上,反之運(yùn)行在 iPhone 上,如下圖所示。

運(yùn)行上述代碼后,效果如下圖所示。可以看到 iOS 和 iPadOS 的代碼被區(qū)分看待。


5、Apple Watch 應(yīng)用

若你希望給應(yīng)用增加 Apple Watch 的手表支持,可以在 Xcode 頂部菜單欄中,選擇「File - New Target」,如下圖所示。

在 watchOS 菜單中,可以看到「Watch App for iOS App」和「Watch App」兩個(gè)選項(xiàng)。這二者的區(qū)分度需要額外留意,其中 for iOS App 指的是該手表應(yīng)用必須依賴于 iOS 應(yīng)用主體發(fā)布,無法單獨(dú)在 Watch 應(yīng)用商店中下載。無論該手表應(yīng)用是否存在與 iOS 應(yīng)用的數(shù)據(jù)互通,這種依賴關(guān)系是必須存在的。而 Watch App 指的是可以單獨(dú)發(fā)布,無需依賴任何 iOS 應(yīng)用,可直接在 Watch 應(yīng)用商店中下載的應(yīng)用。這二者根據(jù)你的實(shí)際需求選擇即可。

選擇完成后,如下圖所示,將 Watch 應(yīng)用添加至 Xcode 項(xiàng)目中。

添加完成后,若你選擇的是 Watch App for iOS App 選項(xiàng),則會(huì)看到下圖左側(cè)多出來了 WatchApp Extension 選項(xiàng)。該文件夾中的內(nèi)容,是 Xcode 已經(jīng)為 Watch 應(yīng)用創(chuàng)建好的模版。以我們熟悉的 ContentView.swift 為例,可以看到如下圖所示的 SwiftUI 視圖代碼。

額外說明一下,你也許會(huì)感到疑惑,Xcode 如何知道項(xiàng)目中相同文件名的文件到底該運(yùn)行哪一份,答案是 Target Membership 運(yùn)行目標(biāo)。如下圖右側(cè)所示,假設(shè)代碼的核心邏輯下載在 Logic.swift 文件中,倘若在右側(cè)面板勾選 WatchApp Extension,則代表這份文件可以在 Watch 應(yīng)用中使用,其它所有文件同理,都具備運(yùn)行目標(biāo)這個(gè)選項(xiàng)。

結(jié)束界面和邏輯的編程后,點(diǎn)擊運(yùn)行欄中的 WatchApp,并在模擬器中選擇一臺(tái)虛擬機(jī)即可運(yùn)行。額外說明的是,若你希望用到真機(jī)測(cè)試,則需要在 Xcode 中的「Window - Devices and Simulators」中,對(duì)手表進(jìn)行與手機(jī)的配對(duì)。之后就可以直接在真機(jī)手表上進(jìn)行測(cè)試了。

將 Watch 虛擬機(jī)一并運(yùn)行,當(dāng)前版本的跨平臺(tái)應(yīng)用已支持 iPad、iPhone、Apple Watch、Mac 這四個(gè)平臺(tái)。


6、Apple TV 應(yīng)用

在海外市場(chǎng)中,Apple TV 應(yīng)用也占據(jù)一席之地。你可以將 TV 應(yīng)用理解為家庭機(jī)頂盒,或運(yùn)行在智能電視中的應(yīng)用,只不過在 Apple 平臺(tái)的硬件載體是 Apple TV。重復(fù)上述 Apple Watch 應(yīng)用的添加方法,這次選擇 TV App 即可創(chuàng)建 Apple TV 應(yīng)用。創(chuàng)建完成后,點(diǎn)擊 Apple TV 模擬機(jī)即可運(yùn)行,如下圖所示。

至此,在同一個(gè) Xcode 項(xiàng)目中添加各平臺(tái)的代碼介紹完畢,如下圖所示。Mac、Apple TV、Apple Watch、iPhone、iPad 應(yīng)用均通過 SwiftUI 界面語(yǔ)言串聯(lián)在一起。


7、判斷 UI 在各平臺(tái)上的可用性

在開發(fā)過程中,你也許會(huì)碰到另一個(gè)問題,我的 SwiftUI 代碼到底哪些可以用。這個(gè)問題的答案寫在 Apple 開發(fā)者文檔中,如下圖右側(cè)所示,在調(diào)用框架時(shí),你需要注意該框架中內(nèi)容的可用性。比如 Toggle 開關(guān)的寫法,支持全平臺(tái)使用。如下圖所示,將一個(gè)最簡(jiǎn)單的開關(guān)代碼放置在跨平臺(tái)文件夾中。

@State private var toggleOn = true 
Toggle("Do Something", isOn: $toggleOn).padding()

對(duì)于視圖有差異的 Apple TV,同理將開關(guān)代碼復(fù)制進(jìn)去,但刪除 iOS 的判斷語(yǔ)句。

準(zhǔn)備完成后運(yùn)行,可以看到如下圖所示的界面。在 Mac、iPhone、Apple Watch、Apple TV 中,同樣的 SwiftUI 開關(guān)寫法,實(shí)際上顯示方式完全不一樣。在 Mac 中是我們熟悉的勾選框,而在 TV 中則是點(diǎn)入式菜單。這也是 SwiftUI 強(qiáng)大的地方,并非追求統(tǒng)一,而是在代碼統(tǒng)一的前提下,保證各個(gè)平臺(tái)體驗(yàn)的獨(dú)立性。

對(duì)于很多應(yīng)用來說,諸如用到 Apple Pencil 的 Procreate,也許純 iPad 應(yīng)用就已經(jīng)是最佳選擇;對(duì)于筆記應(yīng)用如 Bear,用戶確實(shí)有跨平臺(tái)記錄的需求;對(duì)于一款快速查看潮汐的應(yīng)用,也許純 Apple Watch 應(yīng)用已經(jīng)是最佳選擇,畢竟在進(jìn)行這類運(yùn)動(dòng)時(shí)用戶手機(jī)大概不在身邊。


三、桌面小組件 Widgets

桌面小組件 Widget 是 iOS 14、macOS Big Sur 之后,Apple 推出的顯示在桌面上的便捷視圖。現(xiàn)階段,桌面小組件可以被用在 iPhone、iPad、Mac 這三類設(shè)備中。Apple 生態(tài)系統(tǒng)中的小組件以展示信息為核心訴求,僅提供非常輕量的互動(dòng)。若你習(xí)慣了 Android 手機(jī)上的部分復(fù)雜強(qiáng)大的桌面組件,應(yīng)該適當(dāng)降低預(yù)期,Apple 版本的桌面小組件與其還是有較大差異的。

1、背景

以 iPhone 為例,小組件主要集中在設(shè)備的負(fù)一屏,亦可被添加在桌面的任意位置。當(dāng)用戶長(zhǎng)按桌面背景,并點(diǎn)擊左上角的加號(hào)后,可以看到如下圖所示的小組件選擇器。小組件選擇器羅列了當(dāng)前設(shè)備中所有支持的小組件的應(yīng)用,并提供了小組件預(yù)覽,用戶點(diǎn)擊后即可拖拽添加至桌面。

小組件分為三種尺寸,分別是占據(jù) 4 個(gè)應(yīng)用圖標(biāo)格的小尺寸、8 個(gè)應(yīng)用圖標(biāo)格的中尺寸、16 個(gè)應(yīng)用圖標(biāo)格的大尺寸。值得注意的是:對(duì)于小尺寸的桌面小組件來說,除自身顯示的信息外,其本質(zhì)上就僅僅是一個(gè)按鈕。開發(fā)者無法自定義任何小組件中的互動(dòng)模塊,除展示信息外其功能只有一個(gè),就是點(diǎn)擊后跳轉(zhuǎn)到哪里去。而中號(hào)或者大號(hào)應(yīng)用小組件,則支持稍多一些的互動(dòng)選項(xiàng),允許開發(fā)者提供多個(gè)按鈕。


2、系統(tǒng)級(jí)別的功能

WidgetKit 是純 SwiftUI 視圖的框架。得益于 SwiftUI 及諸多系統(tǒng)內(nèi)置的功能,在開發(fā)者什么都不做的前提下,小組件自身已經(jīng)具備了幾項(xiàng)常用功能。本小節(jié)用「自動(dòng)暗色模式」和「小組件推薦輪轉(zhuǎn)」舉例。

如下圖所示,當(dāng)設(shè)備從淺色模式切換至深色模式時(shí),界面中的黃色做出了輕微的版本調(diào)整。所有字體的和背景的顏色也進(jìn)行了反轉(zhuǎn),比如 Steve's Surprise Party 從黑色切換到了白色。對(duì)于這些設(shè)置,只要你的視圖代碼中用到了類似 .color(.primary) 這樣的標(biāo)準(zhǔn)寫法,或用到了系統(tǒng)提供的標(biāo)準(zhǔn)色,以上特性都是自動(dòng)白送的。

如下圖所示,用戶可以將多個(gè)小組件疊加在一起,成為一個(gè)堆疊 Stack。 對(duì)于堆疊在一起的小組件來說,系統(tǒng)會(huì)使用基于 On-Device Intelligence 本地智能的機(jī)器學(xué)習(xí) + 推薦算法,來決定在某個(gè)時(shí)刻,哪一個(gè)小組件會(huì)在屏幕的最上方。其數(shù)據(jù)來源主要是小組件的 Donation 捐贈(zèng),舉個(gè)例子,倘若在廈門的用戶在早上 8:30 分的時(shí)候查看了天氣,那么天氣應(yīng)用很可能捐贈(zèng)了一個(gè)「8:30 查看廈門天氣」的事件。當(dāng)捐贈(zèng)形成規(guī)律后,系統(tǒng)會(huì)將這些捐贈(zèng)當(dāng)作一個(gè)新知識(shí),下次 8:30 分時(shí)自動(dòng)將天氣小組件前置。


3、制作桌面小組件的流程

在 Xcode 中新增小組件的流程與新增 watchOS 應(yīng)用的流程非常相似,都需要新增一個(gè) Target。如下圖所示,在頂部的菜單欄中選擇「File - New -Target」。

在彈出的選項(xiàng)中,選擇「iOS」。接著在「Application Extension」中找到「Widget Extension」并點(diǎn)擊下一步。

此處彈出的是小組件在開發(fā)期間的名稱,根據(jù)你的喜好起名即可,不得與項(xiàng)目名重復(fù)。本例中,使用 WidgetTarget 作為名稱,如下圖所示。

完成以上步驟后,Xcode 會(huì)新建幾份模版文件,其中之前沒見過的文件類型是:WidgetTarget.swiftWidgetTarget.intentdefinitions。在什么內(nèi)容都不添加的情況下,該模版文件已經(jīng)準(zhǔn)備好了一個(gè)時(shí)鐘應(yīng)用,直接運(yùn)行后如下圖所示,顯示當(dāng)前時(shí)間。本文會(huì)專注講解這個(gè)時(shí)鐘應(yīng)用模版中各類代碼的用途。

在上圖中,你也許注意到了系統(tǒng)的實(shí)際時(shí)間是 6:59,而小組件顯示時(shí)間是 6:56,這是因?yàn)樾〗M件的更新不是即時(shí)的,而是由系統(tǒng)根據(jù)開發(fā)者提供的時(shí)間軸來盡量安排。


4、代碼詳解

上述流程走完后,Xcode 模版會(huì)自動(dòng)生成的 WidgetTarget.swift 文件,如下圖所示。

此處的 WidgetTarget.swift 文件名,與之前創(chuàng)建 Widget 時(shí)空格中填寫的命名一致。因每個(gè)人的命名可能不同,你的文件可能不叫 WidgetTarget,但內(nèi)容是一致的,不必?fù)?dān)心。本例中,我將以 WidgetTarget 這個(gè)命名為例子。

WidgetTarget.swift 文件中包含五個(gè)重要的結(jié)構(gòu)體 struct,分別是:ProviderSimpleEntry、WidgetTargetEntryViewWidgetTarget、WidgetTarget_Previews。這些概念相對(duì)較新,大多是 2020 年之后 Apple 發(fā)布的 WidgetKit 中提到的全新概念,因此我會(huì)在下文亂序進(jìn)行逐一解析。

如下圖所示,WidgetTargetEntryView 結(jié)構(gòu)對(duì)應(yīng)的是標(biāo)準(zhǔn)的 SwiftUI 視圖。其中與視圖有關(guān)的代碼寫在 var body: some View { } 中。如下圖所示,Text(entry.date, style: .time) 指的是用 date 這個(gè)時(shí)間戳用于數(shù)據(jù)來源,并將其以 time 時(shí)鐘的方式顯示出來,是一個(gè) Text 純文本視圖。

你也許會(huì)好奇,傳遞數(shù)據(jù)的信息是從哪里來的,答案是 SimpleEntry 這個(gè)結(jié)構(gòu)體。如下圖所示,SimpleEntry 繼承了一個(gè)名為 TimelineEntry 的協(xié)議。對(duì)于小組件來說,存在一個(gè)重要的時(shí)間軸 Timeline 的概念 ,對(duì)于組件的任何一個(gè)時(shí)刻,系統(tǒng)需要知道組件想顯示什么內(nèi)容,并根據(jù)組件的時(shí)間軸進(jìn)行排期。比如一個(gè)日歷應(yīng)用,很可能每天在三個(gè)時(shí)間點(diǎn)各更新一次,每次對(duì)應(yīng)的時(shí)間的信息都不同,每一個(gè)時(shí)間點(diǎn)的信息片段,都存在 SimpleEntry 中。

如下圖所示,WidgetTargetWidgetTarget_Previews 這兩個(gè)結(jié)構(gòu)體負(fù)責(zé)視圖的顯示和 Xcode 窗口預(yù)覽。其中 WidgetTarget_Previews 就不多說了,只是單純地提供該小組件的 Canvas 預(yù)覽窗口,與實(shí)際允許在設(shè)備中的代碼無關(guān)。.previewContext(WidgetPreviewContext(family: .systemSmall)) 是個(gè)新的修改器,指的是用小尺寸的小組件來預(yù)覽。

上圖中 @main 指的是小組件應(yīng)用的接入點(diǎn)。其中 WidgetTargetEntryView(entry: entry) 負(fù)責(zé)把上文中定義的 SwiftUI 視圖 WidgetTargetEntryView 代入最新數(shù)據(jù)并顯示。值得額外介紹的是兩個(gè)新的修改器,此處 .configurationDisplayName("My Widget") 指的是小組件選擇界面的組件名稱,.description("This is an example widget.") 指的是小組件描述,對(duì)應(yīng)內(nèi)容如下圖所示。

最后值得一提的新內(nèi)容便是 Provider 這個(gè)結(jié)構(gòu)體,如下圖所示,繼承了 IntentTimelineProvider。顧名思義,Provider 是一個(gè)提供者,負(fù)責(zé)由小組件向操作系統(tǒng)提供信息。這個(gè)協(xié)議來要求開發(fā)者提供三個(gè)必備函數(shù),分別是 placeholder、getSnapshotgetTimeline。

getPlaceholder 的用途是在沒有網(wǎng)絡(luò)連接,或數(shù)據(jù)還沒能及時(shí)加載的情況下,小組件接收到的默認(rèn)的信息。這一部分信息可以由開發(fā)者直接提供,以淘寶為例,貨物信息默認(rèn)可以是「精選商品」,運(yùn)輸信息可以是「已經(jīng)在路上」。

getSnapshot 則是在諸如小組件選擇界面,屏幕上真實(shí)展示的小組件中,顯示的具備真實(shí)信息的內(nèi)容。舉個(gè)例子,假設(shè)現(xiàn)在是 10:00 分,則 getSnapshot 返回的便是具備 10:00 這個(gè)信息的小組件狀態(tài)。

getTimeline 指的是操作系統(tǒng)向小組件索要的時(shí)間軸。系統(tǒng)需要知道桌面顯示出來的每個(gè)組件,需要在什么時(shí)候進(jìn)行數(shù)據(jù)更新。比如你的小組件顯示的內(nèi)容只是每日精選書摘,那么時(shí)間軸中的內(nèi)容便是「每日更新一次」這個(gè)時(shí)間。系統(tǒng)會(huì)根據(jù)這個(gè)頻率來對(duì)組件內(nèi)容進(jìn)行更新。

請(qǐng)注意,在時(shí)間軸函數(shù)中,開發(fā)者給出的時(shí)間軸只是告訴系統(tǒng)我希望你按照這個(gè)標(biāo)準(zhǔn)刷新小組件。但系統(tǒng)并不保證執(zhí)行,因此這只是君子協(xié)議,實(shí)際刷新時(shí)間由系統(tǒng)根據(jù)當(dāng)前電量、顯示在最上層的組件等信息決定。

上圖中的文件 WidgetTarget.intentdefinition,作用是為小組件提供更復(fù)雜的自定義功能。這一部分內(nèi)容對(duì)于新人有些超綱了,因?yàn)樗婕傲艘恍╆P(guān)于 SiriKit的知識(shí),本教程不做額外擴(kuò)展講解。

以上知識(shí)可以帶你入門,卻不足以憑此構(gòu)建一個(gè)復(fù)雜的小組件應(yīng)用。若你對(duì)創(chuàng)建復(fù)雜桌面小組件感興趣,強(qiáng)烈推薦以下 WWDC 開發(fā)者大會(huì)視頻。

桌面小組件算是過去數(shù)年中,能帶來明顯用戶感知的新系統(tǒng)特性。其核心目的在于展示信息,并非提供深層次互動(dòng)。你可以圍繞該目的,去思考應(yīng)用中哪些信息適合以小組件的形式呈現(xiàn)出來。與此同時(shí),若你的應(yīng)用的確需要的是復(fù)雜的互動(dòng),直接考慮完整應(yīng)用會(huì)更合適一些,切忌把復(fù)雜邏輯交給注重「輕量」的小組件。


Demo

Demo在我的Github上,歡迎下載。
SwiftUIDemo

從零開始實(shí)操完整的小組件項(xiàng)目
小組件設(shè)計(jì)
視圖構(gòu)建及自定義

參考文獻(xiàn)

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。

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

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