🩰

橫向並排 Picker - SwiftUI

2022/12/01に公開

用 SwiftUI 做多選擇的 Picker 的時候發現和 UIKit 不一樣,必須要透過自行建立多個 Pickers 後用 HStack 包起來才可以達成,但是這時候會有排版上的問題。

如下圖一樣,元件會直接往左右凸出去:

image

import SwiftUI

struct ContentView: View {
    
    @State var leftSelectedIndex = 0
    @State var rightSelectedIndex = 0
    
    var body: some View {
        HStack(spacing: 0) {
            // 左 Picker
            Picker("test", selection: $leftSelectedIndex) {
                ForEach(1..<13) { item in
                    Text("\(item)")
                }
            }
            .pickerStyle(.wheel)

            // 右 Picker
            Picker("test", selection: $rightSelectedIndex) {
                ForEach(1..<13) { item in
                    Text("\(item)")
                }
            }
            .pickerStyle(.wheel)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .previewLayout(.fixed(width: 500, height: 300))
    }
}

解法

1. 指定寬度

首先用 GeometryReader 把 HStack 包起來,讓 Pickers 能夠取得 UI 的寬度並設定

GeometryReader { geometry in
    HStack { /* ... */ }
}

在 Picker 加上 .frame() 這個 modifier 來設定寬高

Picker("test", selection: $leftSelectedIndex) {
    ForEach(1..<13) { item in
        Text("\(item)")
    }
}
.pickerStyle(.wheel)
// 設定寬高
.frame(maxWidth: geometry.size.width / 2, maxHeight: geometry.size.height)

Picker("test", selection: $rightSelectedIndex) {
    ForEach(1..<13) { item in
        Text("\(item)")
    }
}
.pickerStyle(.wheel)
// 設定寬高
.frame(maxWidth: geometry.size.width / 2, maxHeight: geometry.size.height)

這時候大小雖然設定正確了,但是發現正中間竟然重疊了:

image

2. 設定裁切

這時候只要加上 .clipped() 這個 modifier 就可以讓 UI 只繪製到指定的邊界以內:

Picker("test", selection: $leftSelectedIndex) {
    ForEach(1..<13) { item in
        Text("\(item)")
    }
}
.pickerStyle(.wheel)
.frame(maxWidth: geometry.size.width / 2, maxHeight: geometry.size.height)
// 設定裁切
.clipped()

Picker("test", selection: $rightSelectedIndex) {
    ForEach(1..<13) { item in
        Text("\(item)")
    }
}
.pickerStyle(.wheel)
.frame(maxWidth: geometry.size.width / 2, maxHeight: geometry.size.height)
// 設定裁切
.clipped()

成果

image

最終的程式碼

import SwiftUI

struct ContentView: View {
    
    @State var leftSelectedIndex = 0
    @State var rightSelectedIndex = 0
    
    var body: some View {
        GeometryReader { geometry in
            HStack(spacing: 0) {
                Picker("test", selection: $leftSelectedIndex) {
                    ForEach(1..<13) { item in
                        Text("\(item)")
                    }
                }
                .pickerStyle(.wheel)
                .frame(maxWidth: geometry.size.width / 2, maxHeight: geometry.size.height)
                .clipped()
                
                Picker("test", selection: $rightSelectedIndex) {
                    ForEach(1..<13) { item in
                        Text("\(item)")
                    }
                }
                .pickerStyle(.wheel)
                .frame(maxWidth: geometry.size.width / 2, maxHeight: geometry.size.height)
                .clipped()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .previewLayout(.fixed(width: 500, height: 300))
    }
}

Discussion