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%。也請按下愛心❤️、留言💬、回信✉️與我交流。這些回饋都會影響到我繼續寫作的動力與內容喔。
我們下一期見!