30
Aug
2023
Multi Column Wheel Picker in SwiftUI
Reading time: 1 min
This recipe shows how to display multiple wheel pickers side-by-side in SwiftUI in a single component.
The end result looks like this:
This solution works for SwiftUI 1+ (iOS 13+, macOS 10.15+).
Here's the full recipe:
- Use a
GeometryReader
to set the width of childPicker
s so that they are evenly distributed into columns. - Put a
clipped
modifier to each picker to modifier to hide any content that extends beyond the layout bounds of the shape. - On iOS 15+, use a
UIPicker
extension that modifier intrinsic content size. Without this, content clipping won't work.
Check out the code for more details:
extension UIPickerView {
open override var intrinsicContentSize: CGSize {
CGSize(width: UIView.noIntrinsicMetric , height: 150)
}
}
struct MultiWheelPicker<Data>: View where Data : Hashable {
let labels: [String]
let data: [[Data]]
@Binding var selection: [Data]
var body: some View {
GeometryReader { geometry in
HStack {
ForEach(0..<data.count) { column in
picker(label: labels[column],
columnData: data[column],
column: column,
geometry: geometry)
}
}
}
}
@ViewBuilder private func picker(
label: String,
columnData: [Data],
column: Int,
geometry: GeometryProxy
) -> some View {
Picker(label, selection: $selection[column]) {
ForEach(0..<columnData.count) { row in
Text(String(describing: columnData[row]))
.tag(columnData[row])
}
}
.pickerStyle(.wheel)
.frame(width: geometry.size.width / CGFloat(self.data.count),
height: geometry.size.height)
.clipped()
Spacer()
}
}
And here's how to put it into action:
struct MultiWheelPickerTest: View {
@State var selection = ["1", "1", "1991"]
var body: some View {
VStack(alignment: .center) {
Text(verbatim: "Selection: \(selection)")
MultiWheelPicker(labels: ["Day", "Month", "Year"],
data: [
Array(1...31).map { "\($0)" },
Array(1...12).map { "\($0)" },
Array(1991...2023).map { "\($0)" }
],
selection: $selection)
.frame(width: 300, height: 300)
}
}
}