Disable specific picker items
Asked Answered
A

1

7

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.

Agma answered 15/1, 2020 at 9:14 Comment(4)
Not sure how this comment is relevant. The view builder is using an Int, as $0.Agma
Same issue here. I remember being able to do this, might be a bug in Xcode/swiftMaestro
Is this still not fixed or available? Or is there another way to do this?Incogitant
@Agma did you ever find a way to achieve this?Jetpropelled
S
1

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)
    }
}
Study answered 26/1, 2022 at 8:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.