簡述 SwiftUI 的 Frame Modifier 和佈局原則
在閱讀這篇文章前,法蘭克希望大家可以將 UIKit 的 frame 的概念先拋到腦後去,好好咀嚼並消化一個全新的概念。UIKit 的 frame 和 SwiftUI 的 frame 是完全不同的概念,UIKit 的 frame 其作用在於對一個 view 設定其位置和大小;而 SwiftUI 的 frame 雖也是被 Apple 歸類在修飾 views 的 modifiers 裡,但其本質上算是一個 view(圖1),且其僅有 size 而沒有 position 的概念。
Apple 將 modifiers 定義為僅可用來修飾 views,且無法單獨存在。那法蘭克何以說 frame 實質上是一個 view 呢?試著在 Text 上疊加一個 frame,其寬等於螢幕,會怎樣呢?如同在 Preview 上所看到的藍框就是這個 frame(圖2),把它想像成一個透明的 view。
到目前為此,大家都知道 SwiftUI 的 frame 被歸類在 modifiers 裡,本質上是一個 view,並且不能單獨存在,僅可用來修飾 views。
在開始解釋 SwiftUI frame 前,先來了解 SwiftUI views 的佈局原則(圖3)。
Step1:當要佈局某個 view 時。
Step2:view 拿著 parent view 的 size 去問 child view 說:「我的 size 是多少?」。
Step3:若是不存在 child view,則返回 parent view’s size;若存在,則返回 child’s 自身行為的 view size。
根據上述的佈局原則,兩點分別提出來說明。1. 在 step2 中所謂的 child view 有可能指的是 SwiftUI 的 views 也有可能是 modifiers。2. 在 step3 中所謂的自身行為指的則是可以決定 size 的 views,包括 Stack、Text、Shape 等等。
與其紙上談兵,倒不如拿一段程式碼並搭配佈局原則來操作看看。
執行這段程式碼,預期中應該會產生一個等同於螢幕寬、自適應文字高且紅色背景的 Text,但….事與願違,透過 Preview 看到得確是…(圖4)。
為什麼會這樣呢?我們再透過 Xcode 的 Hierarchy 來看看圖層疊加的順序(圖5)。
原來啊,佈局順序是由 view 下的 modifier 由下至上的
依這個 case 來看的話。則會是,frame → background → text(圖6)。
套用佈局原則來說明的話,則會是:
- 當要佈局 ContentView 時。
- ContentView 拿著 parent’s view 建議的 size(Hosting view 佔滿螢幕的 size) 去詢問 child view(frame)。
- frame 返回自身行為的 view size。
- ContentView 依 frame 返回的 size,生成一透明的 view(圖7藍框)。
- 當要佈局 background view 時。
- Background view 拿著 parent’s view 建議的 size(frame view 等同螢幕寬的 size) 去詢問 child view(text)。
- Text 返回自身行為的 view size。text 會依其文字內容,生成容納的下文字的 size。
由上述的案例,我們了解到 佈局順序是由下至上的
。
所以,如果要看到預期的結果,也就是紅色背景寬度等同於螢幕寬的話,只要調整一下 background 和 frame 的順序即可(圖8)。
結論
當我們在思考佈局時,有幾個原則和要點或許會帶來更好的開發效率。
- 佈局的思維不會再像是 UIKit 一樣,一個 parent view 有幾個 child view。而更應該是由內而外的,一層包一層,以中心為原點開始思考如何佈局。
- SwiftUI 沒有座標系統,取而代之的是 spacer view 和 padding modifier 來定義 UI 的位置。
- 雖然我們都曉得佈局的原則,但在佈局過程中是不需要反覆操作的,而是,當發現佈局結果與預期的不一致時,再將佈局原則拿出來驗證即可。
如果您喜歡我的文章,請多按幾下「拍手」給我鼓勵,或是按「follow」讓我持續提供好文章給您。若有任何問題也歡迎隨時提出。