Categories
iOS Swift SwiftUI

Collapsible wheel picker for forms in SwiftUI

While working on an app, I needed a way for showing a picker with a wheel style in a form. If we add a wheel picker to a form, it is always shown. Better would be to show a row which describes the current selection and if I tap on the row, it reveals the picker with wheel style. This is exactly what we are going to build in this blog post. Moreover, we will first create a general purpose collapsible view which can be used in other use cases as well. In the end, we will have a collapsible wheel picker which behaves like this:

We start with creating a general purpose collapsible view which has two closures: one for title view and the other for collapsible view. The view implementation is fairly simple. A button controls the collapsed state and then optionally we add the collapsible secondary view. We are using view builders here since we want to construct views with closures which support creating multiple child views. The view is optimized for forms, and therefore we use the Group view which automatically adds a divider between the button and the secondary view. Button uses a plain style which removes the tint colour of it in forms.

struct CollapsibleView<Label, Content>: View where Label: View, Content: View {
@State private var isSecondaryViewVisible = false
@ViewBuilder let label: () -> Label
@ViewBuilder let content: () -> Content
var body: some View {
Group {
Button(action: { isSecondaryViewVisible.toggle() }, label: label)
.buttonStyle(.plain)
if isSecondaryViewVisible {
content()
}
}
}
}

Now we can use this view to create a new CollapsibleWheelPicker. This view just adds a picker with wheel style as the secondary view.

struct CollapsibleWheelPicker<SelectionValue, Content, Label>: View where SelectionValue: Hashable, Content: View, Label: View {
@Binding var selection: SelectionValue
@ViewBuilder let content: () -> Content
@ViewBuilder let label: () -> Label
var body: some View {
CollapsibleView(label: label) {
Picker(selection: $selection, content: content) {
EmptyView()
}
.pickerStyle(.wheel)
}
}
}

A full example looks like this:

struct ContentView: View {
@State private var selection = 1
let items = [0, 1, 2, 3, 4, 5, 6, 7, 8]
var body: some View {
NavigationStack {
Form {
CollapsibleWheelPicker(selection: $selection) {
ForEach(items, id: \.self) { item in
Text("\(item)")
}
} label: {
Text("Cups of Water")
Spacer()
Text("\(selection)")
}
}
}
}
}

If this was helpful, please let me know on Mastodon@toomasvahter or Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.