利用 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。

SwiftUI background / overlay ( ) 和 { } 的差異

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com