Last active
May 24, 2024 23:52
-
-
Save magnuskahr/b534d803a550cbe5dc6b65f573d5af2f to your computer and use it in GitHub Desktop.
A simple picker to pick a enum.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} | |
} |
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))
}
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks @lstomberg!
And yes, it is always great to use things from the Swift Standard Library, good think 💪🏼💪🏼