Picker for optional data type in SwiftUI?

后端 未结 4 1729
一生所求
一生所求 2021-02-12 16:46

Normally I can display a list of items like this in SwiftUI:

enum Fruit {
    case apple
    case orange
    case banana
}

struct FruitView: View {

    @State          


        
4条回答
  •  清歌不尽
    2021-02-12 17:14

    I actually prefer @Senseful's solution for a point solution, but for posterity: you could also create a wrapper enum, which if you have a ton of entity types in your app scales quite nicely via protocol extensions.

    // utility constraint to ensure a default id can be produced
    protocol EmptyInitializable {
        init()
    }
    
    // primary constraint on PickerValue wrapper
    protocol Pickable {
        associatedtype Element: Identifiable where Element.ID: EmptyInitializable
    }
    
    // wrapper to hide optionality
    enum PickerValue: Pickable where Element: Identifiable, Element.ID: EmptyInitializable {
        case none
        case some(Element)
    }
    
    // hashable & equtable on the wrapper
    extension PickerValue: Hashable & Equatable {
        func hash(into hasher: inout Hasher) {
            hasher.combine(id)
        }
        
        static func ==(lhs: Self, rhs: Self) -> Bool {
            lhs.id == rhs.id
        }
    }
    
    // common identifiable types
    extension String: EmptyInitializable {}
    extension Int: EmptyInitializable {}
    extension UInt: EmptyInitializable {}
    extension UInt8: EmptyInitializable {}
    extension UInt16: EmptyInitializable {}
    extension UInt32: EmptyInitializable {}
    extension UInt64: EmptyInitializable {}
    extension UUID: EmptyInitializable {}
    
    // id producer on wrapper
    extension PickerValue: Identifiable {
        var id: Element.ID {
            switch self {
                case .some(let e):
                    return e.id
                case .none:
                    return Element.ID()
            }
        }
    }
    
    // utility extensions on Array to wrap into PickerValues
    extension Array where Element: Identifiable, Element.ID: EmptyInitializable {
        var pickable: Array> {
            map { .some($0) }
        }
        
        var optionalPickable: Array> {
            [.none] + pickable
        }
    }
    
    // benefit of wrapping with protocols is that item views can be common
    // across data sets.  (Here TitleComponent { var title: String { get }})
    extension PickerValue where Element: TitleComponent {
        @ViewBuilder
        var itemView: some View {
            Group {
                switch self {
                    case .some(let e):
                        Text(e.title)
                    case .none:
                        Text("None")
                            .italic()
                            .foregroundColor(.accentColor)
                }
            }
            .tag(self)
        }
    }
    

    Usage is then quite tight:

    Picker(selection: $task.job, label: Text("Job")) {
        ForEach(Model.shared.jobs.optionalPickable) { p in
            p.itemView
        }
    }
    

提交回复
热议问题