Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable specific picker items

Is there a way to disable specific picker items? In AppKit, you can disable NSPopUpButton items via the NSMenuValidation protocol, but disabling a label in the picker predictably does nothing. Just another API gap in SwiftUI?

I tried:

Picker(selection: $viewModel.providerSelection, label: Text("Try using:")) {
    ForEach(0..<Provider.allCases.count) {
        Text(Provider.allCases[$0].rawValue.capitalized)
            .disabled(true)
    }
}

and there was no visual or interaction difference between disabling and not here.

like image 468
Procrastin8 Avatar asked Jan 15 '20 09:01

Procrastin8


1 Answers

This seems currently not supported in pure SwiftUI. However, you can try wrapping NSPopUpButton in NSViewRepresentable, like this:

    /// SwiftUI wrapper for NSPopUpButton which allows items to be disabled, which is currently not supported in SwiftUI's Picker
struct PopUpButtonPicker<Item: Equatable>: NSViewRepresentable {

    final class Coordinator: NSObject {
        private let parent: PopUpButtonPicker

        init(parent: PopUpButtonPicker) {
            self.parent = parent
        }

        @IBAction
        func selectItem(_ sender: NSPopUpButton) {
            let selectedItem = self.parent.items[sender.indexOfSelectedItem]
            print("selected item \(selectedItem) at index=\(sender.indexOfSelectedItem)")
            self.parent.selection = selectedItem
        }
    }

    let items: [Item]
    var isItemEnabled: (Item) -> Bool = { _ in true }
    @Binding var selection: Item
    let titleProvider: (Item) -> String

    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }

    func makeNSView(context: Self.Context) -> NSPopUpButton {
        let popUpButton = NSPopUpButton(frame: .zero, pullsDown: false)
        popUpButton.autoenablesItems = false
        popUpButton.target = context.coordinator
        popUpButton.action = #selector(Coordinator.selectItem(_:))

        for item in items.enumerated() {
            popUpButton.addItem(withTitle: self.titleProvider(item.element))
            // in order for this to work, autoenablesItems must be set to false
            popUpButton.menu?.item(at: item.offset)?.isEnabled = self.isItemEnabled(item.element)
        }

        if let selectedIndex = self.items.firstIndex(where: { $0 == self.selection }) {
            popUpButton.selectItem(at: selectedIndex)
        }

        return popUpButton
    }

    func updateNSView(_ view: NSPopUpButton, context: Self.Context) { }
}


// MARK: - Usage

struct ContentView: View {

    private let items: [Int] = [1, 2, 3, 4, 5]
    @State private var selectedValue: Int = 0

    var body: some View {
        PopUpButtonPicker(items: self.items, isItemEnabled: { $0 != 4 }, selection: self.$selectedValue, titleProvider: String.init)
    }
}
like image 192
Tom Kraina Avatar answered Nov 07 '22 03:11

Tom Kraina