利用 ZStack,overlay & background 堆疊 SwiftUI 畫面
設計 App 畫面時,除了控制元件的上下左右位置,控制元件的階層也很重要,因為它將決定 A 元件會覆蓋元件 B 還是被 B 覆蓋,影響元件是否會被別人檔到。
在 SwiftUI 裡主要有 ZStack,overlay & background 三種技術可以調整階層,接下來讓我們一一介紹吧。
- 將元件一層層疊上的 ZStack。
- 將別人疊在自己身上的 overlay。
- 將別人壓在自己底下的 background。
將元件一層層疊上的 ZStack
ZStack 跟其它 stack 元件一樣,可以在 { } 裡加入它要包含排列的元件。不同於水平排列的 HStack & 垂直排列的 VStack,它會將元件一層層疊上。
就像疊疊樂,{ } 裡愈後面產生的元件將疊在之前的元件身上,因此以下程式會先加入 Image,然後再疊上 Text,變成 Text 覆蓋 Image。
struct ContentView: View {
var body: some View {
ZStack {
Image(.peter)
.resizable()
.scaledToFit()
Text("愛瘋一切為蘋果的彼得潘")
}
}
}
ZStack 控制對齊的 alignment
ZStack 排列時預設會將元件全都置中,但我們可透過它的 alignment 參數控制對齊的方式。
範例。
將 alignment 設為 .bottom,圖片和文字對齊底部。
struct ContentView: View {
var body: some View {
ZStack(alignment: .bottom) {
Image(.peter)
.resizable()
.scaledToFit()
Text("愛瘋一切為蘋果的彼得潘")
}
}
}
ZStack 搭配 padding & offset 調整元件位置
當我們利用 ZStack 堆疊元件時,也常結合 padding & offset 調整元件位置和元件間的間距。
範例。
利用 padding 讓 Image & Text 之間不重疊,利用 offset 將 Text 往右偏移。
struct ContentView: View {
var body: some View {
ZStack(alignment: .bottom) {
Image(.peter)
.resizable()
.scaledToFit()
.padding(.bottom, 30)
Text("愛瘋一切為蘋果的彼得潘")
.offset(x: 50)
}
}
}
將別人疊在自己身上的 overlay
我們可以利用 overlay 將元件疊在另一個元件上,比方以下例子從 Image 呼叫 overlay,傳入 Text,因此 Text 會疊在 Image 身上。
struct ContentView: View {
var body: some View {
Image(.peter)
.resizable()
.scaledToFit()
.overlay {
Text("愛瘋一切為蘋果的彼得潘")
}
}
}
同樣的,overlay 也是預設置中,但我們可透過 alignment 參數控制對齊的方式。
範例。
圖片和文字對齊底部。
struct ContentView: View {
var body: some View {
Image(.peter)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Text("愛瘋一切為蘋果的彼得潘")
}
}
}
我們也可以結合 padding & offset 改變元件的位置。
struct ContentView: View {
var body: some View {
Image(.peter)
.resizable()
.scaledToFit()
.padding(.bottom, 30)
.overlay(alignment: .bottom) {
Text("愛瘋一切為蘋果的彼得潘")
.offset(x: 50)
}
}
}
就像彼得潘可以一次背十幾台 Macbook Pro,享受甜蜜的負荷,SwiftUI 的 view 也可以呼叫多次的 overlay,將多個 view 疊在自己身上。元件堆疊的順序跟 overlay 的順序有關,愈後面呼叫 overlay 加入的元件將疊在之前的元件上。
範例。
Image 呼叫了 2 次 overlay,先疊上 Text,再疊上月亮的 Image。
struct ContentView: View {
var body: some View {
Image(.peter)
.resizable()
.scaledToFit()
.padding(.bottom, 30)
.overlay(alignment: .bottom) {
Text("愛瘋一切為蘋果的彼得潘")
.offset(x: 50, y: 0)
}
.overlay(alignment: .bottom) {
Image(systemName: "moon.fill")
.resizable()
.frame(width: 100, height: 100)
}
}
}
因此階層順序如下,月亮在最上層,然後依序是文字和圖片 peter。
將別人壓在自己底下的 background
overlay 可以貼心地將別人加在自己身上,但如果喜歡欺負別人,我們也可以利用 background 將別人壓在自己底下。
我們可以利用 background 將元件放在另一個元件下,比方以下例子從 Image peter 呼叫 background,傳入 Circle,因此圓形會被可憐地壓在 peter 底下。
struct ContentView: View {
var body: some View {
Image(.peter)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Text("愛瘋一切為蘋果的彼得潘")
}
.background {
Circle()
}
}
}
同樣的,background 也是預設置中,但我們可透過 alignment 參數控制對齊,搭配 padding & offset 調整元件的位置。
struct ContentView: View {
var body: some View {
Image(.peter)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Text("愛瘋一切為蘋果的彼得潘")
}
.background(alignment: .top) {
Circle()
.offset(x: -30)
}
}
}
我們也可以呼叫多次的 background,將多個 view 疊在自己底下。元件堆疊的順序跟 background 的順序有關,愈後面呼叫 background 加入的元件愈慘,將被壓在愈底下,比方以下程式最後加入的圓形將在最底層。
struct ContentView: View {
var body: some View {
Image(.peter)
.resizable()
.scaledToFit()
.overlay(alignment: .bottom) {
Text("愛瘋一切為蘋果的彼得潘")
}
.background(alignment: .topTrailing) {
Image(.yellowPeter)
.offset(x: 50, y: -30)
}
.background(alignment: .top) {
Circle()
}
}
}
background 很適合拿來設定元件的背景顏色,背景圖片 & 漸層背景,有興趣的朋友也可以參考以下連結。
background & ZStack 的選擇
background & ZStack 都可以設定背景,選擇時可考慮以下情況。
- 想在某個 view 底下加入跟它一樣大的元件當背景時,選擇 background。
- 想在多個 view 底下加入背景,或是想加入的背景比顯示的內容大時,選擇 ZStack。