在上一回,我們嘗試了怎麼透過 @ViewBuilder 來作為參數傳入一個 view 。那今天我們要用這個來解決另外一個問題。
在一些畫面中,我們需要根據條件判斷要回傳哪一個 UI 。
像是這樣,當寫出這樣的程式碼的地方,在 itemView
這個方法中會出錯
struct ContentView: View {
enum DeviceType {
case phone
case pad
}
var body: some View {
ScrollView {
VStack(alignment: .center) {
itemView(type: .pad)
itemView(type: .phone)
}
}
}
/* 這邊 */
private func itemView(type: DeviceType) -> some View {
switch type {
case .pad:
// 這一行會出錯
padView()
case .phone:
phoneView()
}
}
/* 這邊 */
private func padView() -> some View {
HStack {
Circle()
.foregroundStyle(.gray)
.frame(width: 50, height: 50)
VStack(alignment: .leading) {
Text("Title")
.font(.headline)
Text("description")
.font(.subheadline)
}
Spacer()
}
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.stroke()
.foregroundStyle(.gray)
)
.padding()
}
private func phoneView() -> some View {
VStack {
Circle()
.foregroundStyle(.gray)
.frame(width: 50, height: 50)
VStack {
Text("Title")
.font(.headline)
Text("description")
.font(.subheadline)
}
}
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.stroke()
.foregroundStyle(.gray)
)
.padding()
}
}
錯誤訊息:
Branches have mismatching types 'some View' (result of 'ContentView.padView()') and 'some View' (result of 'ContentView.phoneView()')
提示在條件分歧的地方,實際型別不同而產生問題
private func itemView(type: DeviceType) -> some View {
Group {
switch type {
case .pad:
padView()
case .phone:
phoneView()
}
}
}
只要用 Group 或 VStack 之類的 UI 元件包起來,錯誤就會消失了。
但是這個解法不是很漂亮,因為加了這個 Group ,對 UI 建構來說可能是一個不需要存在的東西,所以看起來 workaround 感非常重。
這時候就可以使用 @ViewBuilder 來標記這個方法。
改寫之後就會像這樣,方法裡面就只留下必要的邏輯,乾淨許多:
@ViewBuilder private func itemView(type: DeviceType) -> some View {
switch type {
case .pad:
padView()
case .phone:
phoneView()
}
}
除了像是根據裝置不同,提供不一樣的畫面佈局,也可以用在類似於工廠方法的情境。
例如在不同狀態、不同型態下會需要提供不同畫面佈局的時候,就可以在封裝後加上 @ViewBuilder 。
以上,
那今天的 SwiftUI 的大大小小就到這邊,明天見!
本篇使用到的 UI 元件和 modifiers 基本上沒有受到版本更新影響
因此 Xcode 14 等環境下使用也是沒問題的。