13 報

Share this post

SwiftUI 專欄 #5 只要看一眼 body

www.ethanhuang13.com
SwiftUI 專欄

SwiftUI 專欄 #5 只要看一眼 body

SwiftUI 讀完就入坑 2022/08/26

13
Aug 25, 2022
3
Share this post

SwiftUI 專欄 #5 只要看一眼 body

www.ethanhuang13.com

13 的話

SwiftUI 專欄是一個付費專欄,只是我沒有設立付費牆,而是採用「誠實訂閱」制度。

我的目標是累積到 100 位 Patreon 支持者,目前進度來到 27%,感謝新舊朋友們的支持。

如果你是第一次讀這個專欄,建議先回到目錄,照順序閱讀。

離上次發表專欄文章已經過了一個多月,最主要是因為我這陣子工作內容轉變,很少寫 SwiftUI,甚至連程式都很少寫很多😅

在這種情況下要繼續寫出長篇的 SwiftUI 文章 ,有著天然的心理障礙。再加上前陣子休了個假。

不過既然都說是訂閱制的付費專欄,總是不能因為這樣就擺著。用來刺激寫作靈感的各種設備已經在路上了,希望可以好轉(13,你不是又找理由花錢吧?😏)。

可以確定的是,以後的文章篇幅會短一點、較好消化一點。

今天來跟大家聊我都怎麼整理 SwiftUI 的程式碼,讓它好讀好懂。

(看前一期 · 回專欄目錄 · 回首頁)


一目了然的 body

SwiftUI 算是很好寫,尤其是搭配我一開始介紹的 SwiftFormat 等技巧。

但是如果你不注意的話,還是可以寫出很難閱讀、很難維護的程式碼。

由於 SwiftUI 的 View 裡面一定會的部分就是 body,所以我讓程式碼好讀的方式就是──讓 body 一眼就可以被看透!

來,直接看一段程式碼。

看得出來這是什麼畫面嗎?有哪些元件?元件之間排版的關係是怎麼樣子?是不是一眼就看懂了。

如果再搭配妥善完整的 Preview 在旁邊,是不是很容易就把元件與實際呈現的樣連結起來?

這就是我們要的效果!

當然,沒經過鍛鍊的 body(?)絕對不是一開始就長這樣。以下歸納我通常寫一個 SwiftUI 畫面的步驟。

第一階段:快速排版

為了快速調整畫面元件,在開始一個新的 View 時,所有的程式碼會先擠在 body 這個 closure 裡面。

一邊在 body 添加程式碼,一邊對照 Preview 是否符合需求的設計圖。

如果遇到要依照變數來改變顯示方式,我通常會先直接寫死,而不是拉一個變數出來。跟資料有關的東西,可以晚一點再一起處理。

第二階段:拆分 body

等到大部分元件都具備以後,從 Preview 看且來跟設計圖夠像,我就會開始把各個部分命名,擺到 @ViewBuilder private的 var 或 func 裡。

什麼叫「夠像」?通常把字體、顏色、元件間距都指定正確,這時候看著 Preview 就會莫名地有感覺。好像已經做完八成的感覺,其實可能才剛開始寫沒幾分鐘。

這大概是寫 SwiftUI 最有成就感的一件事。

命名就如上面程式碼示範的一樣,要一眼就看懂。

寫 @ViewBuilder 是個好習慣,可以很明顯讓人知道這是一個比較小的 View。

雖然有些回傳單一 View 而沒有條件,像是 appLogo 只有一個 Image,不加 @ViewBuilder 的話,compiler 也完全沒問題。

像這樣:

var appLogo: Image {
  Image("appLogo")
}

但我覺得,每次看到 @ViewBuilder 就知道是子畫面,眼睛不用掃到後面的型別。

而且搞不好以後會需要修改回傳的東西,後面一律寫 some View 也是方便修改。

private 則是因為通常只會在 body 用到這個元件。

總之,我都會寫成這種樣的結構:@ViewBuilder、換行、private var、名稱、回傳型別為 some View。

@ViewBuilder
private var appLogo: some View {
  Image("appLogo")
}

我習慣從 body 開始往下都是一整排 @ViewBuilder,不用特別寫 // MARK: Views 來分程式碼區塊。反正看到沒有寫 @ViewBuilder 的就知道捲到另一個區塊了,算是滿直覺的

那如果從 body 拆分這個子畫面的時,需要傳入變數,我就會把它寫成 func buildFooView 的形式。

此時,func 內部用到的變數就變成從外部注入,有的甚至要寫 if else。這個小小的動作顯得非常自然,但你已經對該元件完成了重要的 refactor。

如果是依照條件來顯示 subView, 可以只寫 if show { subview },不用寫 else { EmptyView() }。這是使用 @ViewBuilder 的另一個好處

body 那邊要不要開始定義 property 來放要注入的變數?如果很簡單的是可以,但我通常還是會晚一點再處理。

抽成 func buildFooView 還有一個好處,就是如果想要單獨 Preview 那個子元件,可以很輕鬆得做到。

第三階段:處理資料流

等到 body 都拆分完以後,可以開始決定畫面上會變動的部分,是要用 @State 安置、把某些資料放到一個或多個 view model 等等。

這個時候 View 的 init 介面會完成。而我通常會把 Preview 裡的畫面複製好幾份,依照我覺得需要看到不同資料對應不同樣貌,而調整 init 注入的參數。

也就是善用 Preview 快速驗證不同資料跟畫面的對應關係。

第四階段:完成其餘功能

最後的步驟就是完成剩下的功能。比如畫面細部的微調、串接到下一層畫面,或是與 SwiftUI 無關的實作。

當然,以上步驟並不是絕對的,只是我歸納自己在開發 SwiftUI 時,自然而然會進行的順序。本篇的重點主要還是在前兩階段,後兩階段的細節有機會可以再補充。不過我相信你真的下去寫 SwiftUI 一陣子以後大概就可以理解,不需要硬記。

結論

一開始的程式碼範例是個「登入」頁面。

裡面由上而下會有 app logo、帳號輸入框、密碼輸入框、登入按鈕、忘記密碼按鈕。相信你光是看 body 的部分,就知道整個畫面的結構。再搭配 Preview(雖然我沒放圖),很快就能掌握整個畫面。

這不只是在團隊開發時需要的好習慣。就算是單人開發,對未來的自己維護這份程式碼也給予很大的便利。

我在判斷一個工程師在程式碼易讀性上的造詣時,如果對方剛好寫了 SwiftUI,看一眼他怎麼安排 body 就差不多足夠。

對於從 body 拆出來的子畫面,通常會有這幾個特徵:

  • @ViewBuilder 並換行

  • private var 放靜態的元件

  • private func 放需要注入參數的元件

  • 一律回傳 some View

你學會了嗎?


喜歡本專欄的讀者,請到 Patreon 訂閱支持我🙏 我的目標是累積到 100 位支持者,目前 27%。也請按下愛心❤️、留言💬、回信✉️與我交流。這些回饋都會影響到我繼續寫作的動力與內容喔。

我們下一期見!

Share this post

SwiftUI 專欄 #5 只要看一眼 body

www.ethanhuang13.com
Comments
TopNewCommunity

No posts

Ready for more?

© 2023 ethanhuang13
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing