Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practical way to validate NSTouchBar items

On AppKit, menu items and toolbar items have validateMenuItem(_:) and validateToolbarItem(_:) respectively. However, by new touch bar items, there is no such convenience method to validate appropriate items at the right moment.

I'm now validating touch bar items every time when I change the related values and invoke a validation method in didSet (see the following sample code). But I feel it is not a good way because the related values must know there is a touch bar item depending on it.

var foo: Foo? {
    didSet {
        if #available(macOS 10.12.1, *), NSClassFromString("NSTouchBar") != nil {
            self.validateTouchBarItem(identifier: .foo)
        }
    }
}


@available(macOS 10.12.1, *)
func validateTouchBarItem(identifier: NSTouchBarItemIdentifier) {

    guard
        let item = self.touchBar?.item(forIdentifier: identifier),
        let button = item.view as? NSButton
        else { return }

    switch identifier {
    case NSTouchBarItemIdentifier.foo:
        button.isEnabled = (self.foo != nil)

    default: break
    }
}

Another way I'm using is the Cocoa-binding and KVO, however, it doesn't always work well.

So, I'm curious whether there is any recommended or a defacto-standard way to validate touch bar items, especially containing NSButton and NSSegmentedControl. I wanna change not only the availability of items but sometimes also images or colors of them depending on the situation. How do you guys validate touch bar items?

like image 641
1024jp Avatar asked Nov 25 '16 06:11

1024jp


1 Answers

I improved my touch bar validation system by myself and made the following protocol and extensions.

@available(macOS 10.12.1, *)
protocol TouchBarItemValidations: class {

    func validateTouchBarItem(_ item: NSTouchBarItem) -> Bool
}



@available(macOS 10.12.1, *)
extension NSTouchBarProvider {

    func validateTouchBarItems() {

        guard NSClassFromString("NSTouchBar") != nil else { return }  // run-time check

        guard let touchBar = self.touchBar else { return }

        // validate currently visible touch bar items
        for identifier in touchBar.itemIdentifiers {
            guard let item = touchBar.item(forIdentifier: identifier) as? NSCustomTouchBarItem else { continue }

            item.validate()
        }
    }

}


@available(macOS 10.12.1, *)
extension NSCustomTouchBarItem: NSValidatedUserInterfaceItem {

    func validate() {

        // validate content control
        if let control = self.control,
            let action = control.action,
            let validator = NSApp.target(forAction: action, to: control.target, from: self)
        {
            if let validator = validator as? TouchBarItemValidations {
                control.isEnabled = validator.validateTouchBarItem(self)

            } else if let validator = validator as? NSUserInterfaceValidations {
                control.isEnabled = (validator as AnyObject).validateUserInterfaceItem(self)
            }
        }
    }



    // MARK: Validated User Interface Item Protocol

    public var action: Selector? {

        return self.control?.action
    }


    public var tag: Int {

        return self.control?.tag ?? 0
    }



    // MARK: Private Methods

    private var control: NSControl? {

        return self.view as? NSControl
    }

}


@available(macOS 10.12.1, *)
extension AppDelegate {

    func applicationDidUpdate(_ notification: Notification) {

        // validate touch bar items
        if #available(macOS 10.12.1, *) {
            if let window = NSApp.mainWindow {
                for responder in sequence(first: window.firstResponder, next: { $0.nextResponder }) {
                    responder.validateTouchBarItems()
                }
            }
        }
    }
}

You should modify AppDelegate's applicationDidUpdate(:_) if you already have one, but except for that, nothing complex to add. You can now use validateTouchBarItem(_:) or normal validateUserInterfaceItem(_:) to validate your own touch bar items!

I suppose this works well at least for my requirements.

like image 89
1024jp Avatar answered Oct 20 '22 18:10

1024jp