iOS 16 推出後,我們可以輕鬆在 SwiftUI 建立一個互動式 (interactive) bottom sheet。我們只需要在 Sheet 視圖嵌入一個 presentationDetents
修飾符就可以了。之前,我們曾經發佈過一篇詳細的教學去介紹這個 API。不過,大家可能已經忘了如何使用這個 API,所以先讓我們看看以下的範例程式碼:
struct BasicBottomSheet: View {
@State private var showSheet = false
var body: some View {
VStack {
Button("Show Bottom Sheet") {
showSheet.toggle()
}
.buttonStyle(.borderedProminent)
.sheet(isPresented: $showSheet) {
Text("This is the expandable bottom sheet.")
.presentationDetents([.medium, .large])
}
Spacer()
}
}
}
我們利用 sheet
修飾符來建立一個 presentation sheet,然後,在 sheet 裡面插入 presentationDetents
修飾符來控制 sheet 的大小。.medium
和 .height
都是內建的 detent。
除了可以調整 sheet 的高度之外,我們沒有什麼可以客製化的。不過,隨著 iOS 16.4 推出,我們有了更多可以客製化的選項,像是建立客製化背景、控制 sheet 的滾動等等。
讓我們看看 presentation sheet 有甚麼客製化的選項吧!請注意,你需要使用 Xcode 14.3(或以上版本),才可以跟著這篇教學進行實作。
客製化 SwiftUI Bottom Sheet 的背景
現在有了新的 presentationBackground
修飾符,我們就可以這樣改變 sheet 的背景顏色:
.presentationBackground(.yellow)
我們也可以應用背景的 material type,來為 sheet 創建模糊效果:
struct ContentView: View {
@State private var showSheet = false
var body: some View {
ZStack(alignment: .top) {
Image("wallpaper")
.resizable()
.scaledToFill()
.clipped()
.ignoresSafeArea()
Button("Show Bottom Sheet") {
showSheet.toggle()
}
.tint(.black)
.buttonStyle(.borderedProminent)
.sheet(isPresented: $showSheet) {
Text("This is the expandable bottom sheet.")
.presentationDetents([ .medium, .large])
.presentationBackground(.thinMaterial)
}
}
}
}
如果我們把 presentation 背景設置為 .thinMaterial
,就可以在 sheet 和背景圖像之間添加一個半透明層。
或者,我們也可以這樣使用修飾符,來把客製化視圖設置為 sheet 的背景:
.presentationBackground {
Image("wallpaper")
}
客製化 Presentation Sheet 的 Corner Radius
這次的更新還有一個新的修飾符 presentationCornerRadius
,讓我們可以調整 presentation sheet 的 corner radius。讓我們看看以下例子:
啟用與背景視圖互動
在之前的 iOS 版本,SwiftUI 會禁用 bottom sheet 後面的視圖,如此一來,使用者就需要先關閉 sheet,才可以與背景視圖互動。從 iOS 16.4 開始,SwiftUI 引入了另一個修飾符 presentationBackgroundInteraction
,讓開發者可以選擇啟用或禁用互動。
要啟用背景視圖互動,我們可以把 presentationBackgroundInteraction
修飾符附加到 sheet 視圖,並把值設置為 .enabled
:
.presentationBackgroundInteraction(.enabled)
在這個情況下,使用者就可以與 sheet 後面的視圖互動了。
我們也可以利用 detent 限制背景視圖互動。例如,bottom sheet 有 3 個不同的 intent:
.presentationDetents([ .height(100), .medium, .large])
如果我們只想使用者與某個 intent 的背景視圖互動,我們可以這樣設置 presentationBackgroundInteraction
修飾符:
.presentationBackgroundInteraction(.enabled(upThrough: .height(100)))
定義滾動行為
在預設情況下,如果 bottom sheet 中包含滾動視圖,當使用者在滾動視圖向上滑動時,sheet 就會擴展到下一個 detent。如果你不明白我的意思,我們可以來看看另一個例子。
假設我們這樣在 bottom sheet 顯示了一個項目列表:
.sheet(isPresented: $showSheet) {
List(1...20, id: \.self) { index in
Text("Item \(index)")
}
.presentationDetents([.medium, .large])
}
當我們開啟一個 bottom sheet 時,它會被帶到 medium detent。但是,我們無法滾動列表。當我們想在滾動視圖向上滑動時,sheet 就會擴展到全螢幕。
如果想讓使用者在 sheet 半打開的狀態下滾動列表,我們可以嵌入 presentationContentInteraction
修飾符,並將其值設置為 .scrolls
:
.sheet(isPresented: $showSheet) {
List(1...20, id: \.self) { index in
Text("Item \(index)")
}
.presentationDetents([.medium, .large])
.presentationContentInteraction(.scrolls)
}
如此一來,即使 sheet 是半打開的狀態,使用者都可以向上或向下滾動列表:
使用者也可以按住並拖動 drag indicator,來調整 sheet 的大小。
先睹為快
在編寫這篇文章時,Apple 仍在測試 iOS 16.4 的 Beta 版,但預計很快就會正式推出。如果你想試試最新的 SwiftUI 更新,必定要下載 Xcode 14.3 (beta)。