Skip to content

Instantly share code, notes, and snippets.

@disc0infern0
Last active December 4, 2021 19:27
Show Gist options
  • Save disc0infern0/8541fd5735ff014a05f2b6c3eb6c9267 to your computer and use it in GitHub Desktop.
Save disc0infern0/8541fd5735ff014a05f2b6c3eb6c9267 to your computer and use it in GitHub Desktop.
Drill down picker for Caseiterable & RawRepresentable collections
// Created by Andrew Cowley on 01/12/2021.
//
import SwiftUI
enum Priority: String, Pickable {
case none, low, medium, high // any raw type can be used
}
struct ContentView: View {
@State var priority: Priority = .none
let link = Image(systemName: "chevron.right")
var body: some View {
NavigationView {
Form {
Section {
// Custom picker:
InsetPicker("Priority", selection: $priority)
// Compare with Apple picker code:
Picker("Priority", selection: $priority) {
ForEach(Priority.allCases) { p in
Text(p.description).tag(p)
}
}
}
} .navigationTitle("Details")
}
}
}
/// Drill down picker with a grouped list style
struct InsetPicker<T: Pickable>: View where T.AllCases: RandomAccessCollection {
/* Argument */
@Binding var selectedValue: T
let title: String
/* Configuration */
@State private var showPicker = false
var body: some View {
formText
// Add .padding to create space for the chevron overlayed by navlink
.padding(.trailing, 20)
.overlay {
NavigationLink("", isActive: $showPicker ) {
listPicker
}
}
}
/// The row on the calling view that will be clicked to trigger the picker
var formText: some View {
HStack {
Text("\(title)")
Spacer()
Text("\(selectedValue.description)")
.foregroundColor(Color.secondary)
}
}
/// Picker styled on an insetGrouped List
var listPicker: some View {
List {
ForEach(T.allCases) { t in
HStack {
Text(t.description)
Spacer()
checkmark(for: t)
}
// Overlay an invisible button across each row
.overlay(selectionButton(t: t))
}
}
.listStyle(.insetGrouped)
.toolbar {
ToolbarItem(placement: .principal) {
Text(title)
}
}
}
func selectionButton(t: T) -> some View {
Button(" ") {
// Replicate slightly annoying feature where the current row cannot
// be selected to dismiss the picker
guard selectedValue != t else { return }
selectedValue = t
showPicker = false
}
}
/// Return a checkmark if the supplied value matches the selectedValue
func checkmark(for t: T) -> some View {
Group {
if t == selectedValue {
Text(Image(systemName: "checkmark"))
.foregroundColor(Color.accentColor)
.bold()
} else {
EmptyView()
}
}
}
init(_ title: String, selection v: Binding<T>) {
self.title = title
self._selectedValue = v
self.showPicker = true
}
}
protocol Pickable: Identifiable, Equatable, CaseIterable, RawRepresentable {
var description: String {get}
}
extension Pickable {
var id: String { "\(self.rawValue)" }
var description: String { "\(self.rawValue)".capitalized }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment