7

I'm currently implementing the NSTouchBar api to my macOS application.

At this moment, the only touch bar I have has the main View Controller as its delegate and I can add items to it fine. The catch is, I need some of those items to appear only when a certain condition is met (a row is selected in a table).

Consider I have a boolean that indicates whether or not the button should be visible. How do I update the NSTouchBar on the fly to show/hide this button in case my boolean changes? (I don't need to observe this boolean, I could simply make the call to update in another method I already implemented)

What I did for now is the following: in touchBar(:makeItemForIdentifier), I have a switch for all identifiers, and under the proper case, I either return the NSCustomTouchBarItem with the button, or nil if my boolean is false.

I tried calling makeTouchBar again after a row of the table is selected but it doesn't update the buttons' visibility, as if touchBar(:makeItemForIdentifier) is not called again.

Thanks!

beeb
  • 1,187
  • 11
  • 32

2 Answers2

10

Four ideas:

  1. Try changing your touch bar's defaultItemIdentifiers to the set of item identifiers that should be shown. Note that this would be problematic if the user has customized the touch bar, but I think swapping items on-demand and customizing the touch bar doesn't go well together anyway. This also has the advantage that you don't need to return nil in touchBar(:makeItemForIdentifier:).
  2. Calling makeTouchBar() will create a new NSTouchBar instance, but not change the touchBar property. Try something like

    viewController.touchBar = viewController.makeTouchBar()
    

    or

    viewController.touchBar = nil
    
    1. Set the touchBar property on the NSTableRowView that should show extra items when selected, and make sure to include the otherItemsProxy in your defaultItemIdentifiers. As the contents of the touch bar are comprised of all elements in the responder chain, this might include the touchBar property of the table row (provided it can become first responder).

    2. Are you sure that these items should be hidden when the row is not selected? Consider disabling them instead (e.g. by setting the enabled property of the buttons they contain to false).

MrMage
  • 7,282
  • 2
  • 41
  • 71
  • 1
    Thanks for a detailed answer. I will try the various propositions and report back. I can already say that 4. is not the best option for me because of the geometric constraints of the bar (i.e. limited amount of visible elements allowed). I might want to minimize space waste in that regard. – beeb Nov 23 '16 at 12:19
  • What you might also be able to do is always have the items in the bar, but with a very low visibility priority. When the items become important, just increase their visibility priority. – MrMage Nov 23 '16 at 12:22
  • Reporting back to happily say that 2. did the trick! In combination with returning `nil` in `touchBar(:makeItemForIdentifier:)` when the item should be hidden. Other solutions might work as well. – beeb Nov 23 '16 at 12:24
  • I see one problem left with the second solution (returning `nil` for hidden elements). If the user customizes the touch bar while these items should be disabled, they won't see a preview of the button in the customization interface. In the end, I think I'll use 4. (disable button), which works well too. – beeb Nov 23 '16 at 15:49
8

Just invalidate the touchbar in your view controller:

self.touchbar = nil

The delegate method makeTouchBar() will then automatically be called. Using your flags, you can easily choose the icons to appear.

EDIT: this solution has been tested and works fine.

vomi
  • 993
  • 8
  • 18
  • thanks for the input but MrMage already said so in his answer (point 2) – beeb Nov 28 '16 at 11:18
  • Edited my answer to reflect the fact that it has been tested. The first answer were ideas. – vomi Nov 28 '16 at 15:45
  • Thanks! This will work fine unless the touch bar is customizable. I don't think there is an elegant way to hide items from the bar temporarily if the bar is customizable. Either the previews will fail in the customization menu if you don't return an item in `touchBar(:makeItemForIdentifier:)` (we could use a parameter in this method to know if we should return the item for the actual bar or the customization menu previews) or you will not have them at all in the customization menu if you don't include them in `makeTouchBar()`. This is a macOS issue though. – beeb Nov 28 '16 at 16:24
  • 1
    Could you add how you've implemented the Touch Bar in the first place? For me this solution didn't work, I had to call `self.view.window?.windowController?.touchBar = nil` for it to work. – Codey Jun 17 '20 at 18:16
  • Made this purely in code, in each ViewController. `override func makeTouchBar() -> NSTouchBar? { let touchBar = NSTouchBar() touchBar.delegate = self touchBar.defaultItemIdentifiers = [NSTouchBarItem.Identifier.fixedSpaceLarge, .touchBarAddButtonIdentifier, .touchBarRemoveButtonIdentifier] return touchBar }` – vomi Jun 19 '20 at 07:31
  • Codey bless you. – Nebojsa Nadj Nov 26 '20 at 00:04