My goal is to have two pickers placed side-by-side horizontally with each picker taking up half the width of the screen. Imagine a UIPickerView
that fits the width of the screen and has two components of equal width - that's what I'm attempting to recreate in SwiftUI.
Since pickers in SwiftUI do not currently allow for multiple components, the obvious alternative to me was just to place two pickers inside an HStack
.
Here's some example code from a test project:
struct ContentView: View {
@State var selection1: Int = 0
@State var selection2: Int = 0
@State var integers: [Int] = [0, 1, 2, 3, 4, 5]
var body: some View {
HStack {
Picker(selection: self.$selection1, label: Text("Numbers")) {
ForEach(self.integers) { integer in
Text("\(integer)")
}
}
Picker(selection: self.$selection2, label: Text("Numbers")) {
ForEach(self.integers) { integer in
Text("\(integer)")
}
}
}
}
}
And here is the canvas:
The pickers do not resize to be half the width of the screen like I would expect. They retain their size and instead stretch the width of the content view, distorting the widths of other UI elements in the process (as I found out when I tried to do this in my other project).
I know that I can use UIViewRepresentable
to get the effect that I want, but SwiftUI would be much easier to use given the complexity of what I'm trying to use this for.
Is it a bug that placing two pickers inside an HStack
does not properly resize them, or do pickers in SwiftUI just have a fixed width that cannot be changed?
Using GeometryReader
, I've managed to get closer to resizing the pickers how I want, but not all the way.
Side note: you can also achieve this same imperfect result without using GeometryReader
by simply setting the frame on each picker to .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.
Here's the example code:
struct ContentView: View {
@State var selection1: Int = 0
@State var selection2: Int = 0
@State var integers: [Int] = [0, 1, 2, 3, 4, 5]
var body: some View {
GeometryReader { geometry in
HStack(spacing: 0) {
Picker(selection: self.$selection1, label: Text("Numbers")) {
ForEach(self.integers) { integer in
Text("\(integer)")
}
}
.frame(maxWidth: geometry.size.width / 2)
Picker(selection: self.$selection2, label: Text("Numbers")) {
ForEach(self.integers) { integer in
Text("\(integer)")
}
}
.frame(maxWidth: geometry.size.width / 2)
}
}
}
}
And here is the canvas:
Pickers in HStack with GeometryReader
The pickers are now closer to having the appearance that I want, but the sizing is still slightly off, and they're now overlapping each other in the middle.
The overlapping in the middle you can fix by adding a clipped() modifier. As for the width, I see them both exactly the same:
struct ContentView: View {
@State var selection1: Int = 0
@State var selection2: Int = 0
@State var integers: [Int] = [0, 1, 2, 3, 4, 5]
var body: some View {
GeometryReader { geometry in
HStack(spacing: 0) {
Picker(selection: self.$selection1, label: Text("Numbers")) {
ForEach(self.integers) { integer in
Text("\(integer)")
}
}
.frame(maxWidth: geometry.size.width / 2)
.clipped()
.border(Color.red)
Picker(selection: self.$selection2, label: Text("Numbers")) {
ForEach(self.integers) { integer in
Text("\(integer)")
}
}
.frame(maxWidth: geometry.size.width / 2)
.clipped()
.border(Color.blue)
}
}
}
}