簡述 SwiftUI 的 Frame Modifier 和佈局原則

法蘭克的 iOS 世界
5 min readJan 21, 2021

--

Copy from Pinterest

在閱讀這篇文章前,法蘭克希望大家可以將 UIKit 的 frame 的概念先拋到腦後去,好好咀嚼並消化一個全新的概念。UIKit 的 frame 和 SwiftUI 的 frame 是完全不同的概念,UIKit 的 frame 其作用在於對一個 view 設定其位置和大小;而 SwiftUI 的 frame 雖也是被 Apple 歸類在修飾 views 的 modifiers 裡,但其本質上算是一個 view(圖1),且其僅有 size 而沒有 position 的概念。

圖1 - SwiftUI frame

Apple 將 modifiers 定義為僅可用來修飾 views,且無法單獨存在。那法蘭克何以說 frame 實質上是一個 view 呢?試著在 Text 上疊加一個 frame,其寬等於螢幕,會怎樣呢?如同在 Preview 上所看到的藍框就是這個 frame(圖2),把它想像成一個透明的 view。

圖2 - SwiftUI frame

到目前為此,大家都知道 SwiftUI 的 frame 被歸類在 modifiers 裡,本質上是一個 view,並且不能單獨存在,僅可用來修飾 views。

在開始解釋 SwiftUI frame 前,先來了解 SwiftUI views 的佈局原則(圖3)。

圖3 - SwiftUI 佈局原則

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)。

圖4

為什麼會這樣呢?我們再透過 Xcode 的 Hierarchy 來看看圖層疊加的順序(圖5)。

圖5 - 圖層的疊加順序

原來啊,佈局順序是由 view 下的 modifier 由下至上的

依這個 case 來看的話。則會是,frame → background → text(圖6)。

圖6

套用佈局原則來說明的話,則會是:

  1. 當要佈局 ContentView 時。
  2. ContentView 拿著 parent’s view 建議的 size(Hosting view 佔滿螢幕的 size) 去詢問 child view(frame)。
  3. frame 返回自身行為的 view size。
  4. ContentView 依 frame 返回的 size,生成一透明的 view(圖7藍框)。
  5. 當要佈局 background view 時。
  6. Background view 拿著 parent’s view 建議的 size(frame view 等同螢幕寬的 size) 去詢問 child view(text)。
  7. Text 返回自身行為的 view size。text 會依其文字內容,生成容納的下文字的 size。
圖7

由上述的案例,我們了解到 佈局順序是由下至上的

所以,如果要看到預期的結果,也就是紅色背景寬度等同於螢幕寬的話,只要調整一下 background 和 frame 的順序即可(圖8)。

圖8

結論

當我們在思考佈局時,有幾個原則和要點或許會帶來更好的開發效率。

  1. 佈局的思維不會再像是 UIKit 一樣,一個 parent view 有幾個 child view。而更應該是由內而外的,一層包一層,以中心為原點開始思考如何佈局。
  2. SwiftUI 沒有座標系統,取而代之的是 spacer view 和 padding modifier 來定義 UI 的位置。
  3. 雖然我們都曉得佈局的原則,但在佈局過程中是不需要反覆操作的,而是,當發現佈局結果與預期的不一致時,再將佈局原則拿出來驗證即可。

如果您喜歡我的文章,請多按幾下「拍手」給我鼓勵,或是按「follow」讓我持續提供好文章給您。若有任何問題也歡迎隨時提出。

--

--