-
-
Save magnuskahr/b534d803a550cbe5dc6b65f573d5af2f to your computer and use it in GitHub Desktop.
| import SwiftUI | |
| struct EnumPicker<T: Hashable & CaseIterable, V: View>: View { | |
| @Binding var selected: T | |
| var title: String? = nil | |
| let mapping: (T) -> V | |
| var body: some View { | |
| Picker(selection: $selected, label: Text(title ?? "")) { | |
| ForEach(Array(T.allCases), id: \.self) { | |
| mapping($0).tag($0) | |
| } | |
| } | |
| } | |
| } | |
| extension EnumPicker where T: RawRepresentable, T.RawValue == String, V == Text { | |
| init(selected: Binding<T>, title: String? = nil) { | |
| self.init(selected: selected, title: title) { | |
| Text($0.rawValue) | |
| } | |
| } | |
| } |
Thanks @lstomberg!
And yes, it is always great to use things from the Swift Standard Library, good think 💪🏼💪🏼
Instead of extensions, you can use a simplified version with a default "displayer", note no need for RawRepresentable, just Hashable and CaseIterable
struct CaseIterablePicker<T: CaseIterable & Hashable> : View
where T.AllCases: RandomAccessCollection {
var title: String = ""
var selection: Binding<T>
var display: (T) -> String = { "\($0)" }
var body: some View {
Picker(title, selection: selection) {
ForEach(T.allCases, id:\.self) {
Text(display($0)).tag($0)
}
}
}
}You can pass closures to display, or KeyPath's with Swift 5.2+, which allows the following, which IMO is better as it simplifies the generic definition greatly and moves the burden on "how to display stuff" to the caller, but in a pretty easy to manage manner
// Simple case, use existing string transforms
enum TestEnum: String, CaseIterable {
case one, two, three, four, five
}
CaseIterablePicker(selection: $testEnum, display: \.rawValue.capitalized) // Options show as "One", "Two", "Three"...// Custom extension to String for camel case transform
enum TestEnum2: String, CaseIterable {
case aTestCase, anotherTestCase, thirdTestCase
}
extension String {
var camelCaseToReadable: String { ... }
}
CaseIterablePicker(selection: $testEnum2, display: \.rawValue.camelCaseToReadable.capitalizingFirstLetter)
// long chained KeyPath, options show as "A test case", "Another test case", "Third test case"// Computed var on enum itself is just as simple and doesn't even require RawRepresentable
enum TestEnum3: CaseIterable {
case someCase, anotherCase, whatever
var localized: LocalizedStringKey { ... }
}
CaseIterablePicker(selection: $testEnum2, display: \.localized)Hey @apocolipse!
That is also a take on using enum for pickers. If a simple text view is what always is needed, I think it is a great solution, however it misses the view flexibility of my version :-)
Also, the best of both world can be done! See this extension for my version:
extension EnumPicker where V == Text {
init(selected: Binding<T>, title: String? = nil, display: @escaping (T) -> String) {
self.init(selected: selected, title: title) {
Text(display($0))
}
}
}
This works great.
The only change I made is to use Apple's standard
CustomStringConvertableprotocol instead ofDisplayable.