In a catalyst Swift project a UIViewController subclass creates a TouchBar like this:
override func makeTouchBar() -> NSTouchBar? {
let touchBar = NSTouchBar()
touchBar.delegate = self
touchBar.defaultItemIdentifiers = [<<some>>]
touchBar.customizationAllowedItemIdentifiers = [<<more>>]
touchBar.customizationIdentifier = "com.me.myapp.touchbar"
return touchBar
}
The default items show up on the TouchBar, but there is no menu item to customize them. Reading the TouchBar documentation there are two options:
The Customization Menu Item A user invokes the customization UI for a particular NSTouchBar object, when it is visible in the Touch Bar, by choosing the bar customization menu item. To enable this menu item you must explicitly opt-in, which you can do in the following ways:
- If you want the system to automatically name, place, validate, and activate this menu item in your app’s menus, set the isAutomaticCustomizeTouchBarMenuItemEnabled property of your app object (of type NSApplication) to true.
- To explicitly place the customization menu item in one of your app’s menus, employ the toggleTouchBarCustomizationPalette(_:) method of your app object. When you do this, the system still names and validates the menu item, and hides it on systems that do not have a Touch Bar.
Trying to do either of those yields a 'NSApplication' is unavailable in Mac Catalyst
error.
So after some googling around I found this:
NSClassFromString("NSApplication")?.setValue(true, forKeyPath: "sharedApplication.automaticCustomizeTouchBarMenuItemEnabled")
print(NSClassFromString("NSApplication")?.value(forKeyPath: "sharedApplication.isAutomaticCustomizeTouchBarMenuItemEnabled"))
This compiles and executes fine. It even prints out Optional(1)
where there was an Optional(0)
before. But the menu item still is not there. I tried this at the point where I generate the TouchBar, where I generate the Menu, and in func application(_, didFinishLaunchingWithOptions)
, but to no avail.
I cannot get the second one to not give me an error.
Curiously the NSTouchBar has the same property, but running
NSTouchBar.isAutomaticCustomizeTouchBarMenuItemEnabled = true
at any point (which compiles, too) yields no difference.
Is this a missing feature or is there a way to achieve it. And if so: how?
I am customizing the menu bar. Due to an error, setting the same shortcut for two options, causes the following output in Xcode:
2020-02-15 14:08:01.492510+0100 Myapp[71888:2189305] [MenuBuilder] Menu has duplicates --
<_UIImmutableKeyCommand: 0x60000374e700> -> Title: Toggle Center Column Action: executeOptionForKeyCommand: Input: 1 + (UIKeyModifierCommand)
<_UIImmutableKeyCommand: 0x60000374e640> -> Title: Toggle Left Column Action: executeOptionForKeyCommand: Input: 1 + (UIKeyModifierCommand)
Make a symbolic breakpoint at _UIMenuBuilderError to catch this in the debugger.
2020-02-15 14:08:01.492701+0100 Myapp[71888:2189305] *** Assertion failure in -[_UIMenuBuilder _insertMenu:intoParentMenu:newParentMenu:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore/UIKit-3901.4.905/UIMenu/UIMenuBuilder.m:456
2020-02-15 14:08:01.492950+0100 Myapp[71888:2189305] [General] inserted menu has duplicate submenu, command or key command, or a key command is missing input or action
2020-02-15 14:08:01.497025+0100 Myapp[71888:2189305] [General] (
0 CoreFoundation 0x00007fff2f3858ab __exceptionPreprocess + 250
1 libobjc.A.dylib 0x00007fff6563f805 objc_exception_throw + 48
2 CoreFoundation 0x00007fff2f3aed10 +[NSException raise:format:arguments:] + 88
3 Foundation 0x00007fff31aa7241 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
4 UIKitCore 0x00007fff6d31dded -[_UIMenuBuilder _insertMenu:intoParentMenu:newParentMenu:] + 1426
5 UIKitCore 0x00007fff6d31e9ef -[_UIMenuBuilder insertChildMenu:atStartOfMenuForIdentifier:] + 177
6 Myapp 0x000000010010a87b $s5Myapp11AppDelegateC9buildMenu4withySo13UIMenuBuilder_p_tF + 4987
7 Myapp 0x000000010010b150 $s5Myapp11AppDelegateC9buildMenu4withySo13UIMenuBuilder_p_tFTo + 64
8 UIKitCore 0x00007fff6d31c428 -[UIResponder _buildMenuFromChainWithBuilder:] + 115
9 UIKitCore 0x00007fff6d31c17f -[UIMenuSystem _newBuilderFromResponderChain:] + 72
10 UIKitCore 0x00007fff6d31c114 -[UIMenuSystem _automaticallyRebuildIfNeeded] + 88
11 UIKitCore 0x00007fff6d31c09e -[UIMenuSystem _rootMenu] + 27
12 UIKitCore 0x00007fff6d3144d9 -[_UIMenuBarController _rebuildRootCommandGroup] + 66
13 Foundation 0x00007fff3197ffab -[__NSObserver _doit:] + 296
14 CoreFoundation 0x00007fff2f2ff35f __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
15 CoreFoundation 0x00007fff2f2ff2f3 ___CFXRegistrationPost1_block_invoke + 63
16 CoreFoundation 0x00007fff2f2ff268 _CFXRegistrationPost1 + 372
17 CoreFoundation 0x00007fff2f2feebe ___CFXNotificationPost_block_invoke + 97
18 CoreFoundation 0x00007fff2f2ce7e2 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1575
19 CoreFoundation 0x00007fff2f2cdc82 _CFXNotificationPost + 1351
20 Foundation 0x00007fff319e6048 postQueueNotifications + 718
21 CoreFoundation 0x00007fff2f3090ee __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
22 CoreFoundation 0x00007fff2f309014 __CFRunLoopDoObservers + 457
23 CoreFoundation 0x00007fff2f308832 __CFRunLoopRun + 1514
24 CoreFoundation 0x00007fff2f307bd3 CFRunLoopRunSpecific + 499
25 HIToolbox 0x00007fff2de5d65d RunCurrentEventLoopInMode + 292
26 HIToolbox 0x00007fff2de5d39d ReceiveNextEventCommon + 600
27 HIToolbox 0x00007fff2de5d127 _BlockUntilNextEventMatchingListInModeWithFilter + 64
28 AppKit 0x00007fff2c4cdba4 _DPSNextEvent + 990
29 AppKit 0x00007fff2c4cc380 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1352
30 AppKit 0x00007fff2c4be09e -[NSApplication run] + 658
31 AppKit 0x00007fff2c490465 NSApplicationMain + 777
32 AppKit 0x00007fff2c7b369c _NSApplicationMainWithInfoDictionary + 16
33 UIKitMacHelper 0x00007fff602de8f1 UINSApplicationMain + 322
34 UIKitCore 0x00007fff6d186273 UIApplicationMain + 2105
35 Myapp 0x000000010010b39b main + 75
36 libdyld.dylib 0x00007fff669ad7fd start + 1
)
2020-02-15 14:08:27.979587+0100 Myapp[71888:2189305] [Layout] Unable to simultaneously satisfy constraints:
(
"<NSAutoresizingMaskLayoutConstraint:0x600002183ac0 h=-&- v=-&- _SC_RESULTS_TABLE.minY == 0 (active, names: _SC_RESULTS_TABLE:0x100dc3140, '|':SCTMenuView:0x100dca2e0 )>",
"<NSAutoresizingMaskLayoutConstraint:0x600002183f70 h=-&- v=-&- V:|-(202)-[_SC_RESULTS_TABLE] (active, names: _SC_RESULTS_TABLE:0x100dc3140, '|':SCTMenuView:0x100dca2e0 )>",
"<NSAutoresizingMaskLayoutConstraint:0x60000217f200 h=--& v=--& SCTMenuView:0x100dca2e0.height == 59 (active)>"
)
Will attempt to recover by breaking constraint
<NSAutoresizingMaskLayoutConstraint:0x600002183f70 h=-&- v=-&- V:|-(202)-[_SC_RESULTS_TABLE] (active, names: _SC_RESULTS_TABLE:0x100dc3140, '|':SCTMenuView:0x100dca2e0 )>
Set the NSUserDefault NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints to YES to have -[NSWindow visualizeConstraints:] automatically called when this happens. And/or, set a symbolic breakpoint on LAYOUT_CONSTRAINTS_NOT_SATISFIABLE to catch this in the debugger.
2020-02-15 14:08:29.504823+0100 Myapp[71888:2189305] [MenuBuilder] Menu has duplicates --
<_UIImmutableKeyCommand: 0x60000375e400> -> Title: Toggle Center Column Action: executeOptionForKeyCommand: Input: 1 + (UIKeyModifierCommand)
<_UIImmutableKeyCommand: 0x60000375e340> -> Title: Toggle Left Column Action: executeOptionForKeyCommand: Input: 1 + (UIKeyModifierCommand)
Make a symbolic breakpoint at _UIMenuBuilderError to catch this in the debugger.
2020-02-15 14:08:29.504936+0100 Myapp[71888:2189305] *** Assertion failure in -[_UIMenuBuilder _insertMenu:intoParentMenu:newParentMenu:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore/UIKit-3901.4.905/UIMenu/UIMenuBuilder.m:456
2020-02-15 14:08:29.506015+0100 Myapp[71888:2189305] [General] inserted menu has duplicate submenu, command or key command, or a key command is missing input or action
2020-02-15 14:08:29.514279+0100 Myapp[71888:2189305] [General] (
0 CoreFoundation 0x00007fff2f3858ab __exceptionPreprocess + 250
1 libobjc.A.dylib 0x00007fff6563f805 objc_exception_throw + 48
2 CoreFoundation 0x00007fff2f3aed10 +[NSException raise:format:arguments:] + 88
3 Foundation 0x00007fff31aa7241 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
4 UIKitCore 0x00007fff6d31dded -[_UIMenuBuilder _insertMenu:intoParentMenu:newParentMenu:] + 1426
5 UIKitCore 0x00007fff6d31e9ef -[_UIMenuBuilder insertChildMenu:atStartOfMenuForIdentifier:] + 177
6 Myapp 0x000000010010a87b $s5Myapp11AppDelegateC9buildMenu4withySo13UIMenuBuilder_p_tF + 4987
7 Myapp 0x000000010010b150 $s5Myapp11AppDelegateC9buildMenu4withySo13UIMenuBuilder_p_tFTo + 64
8 UIKitCore 0x00007fff6d31c428 -[UIResponder _buildMenuFromChainWithBuilder:] + 115
9 UIKitCore 0x00007fff6d31c17f -[UIMenuSystem _newBuilderFromResponderChain:] + 72
10 UIKitCore 0x00007fff6d31c114 -[UIMenuSystem _automaticallyRebuildIfNeeded] + 88
11 UIKitCore 0x00007fff6d3cf5aa -[UIMenuSystem _keyCommands] + 27
12 UIKitCore 0x00007fff6d3cf375 -[UIApplication _keyCommands] + 105
13 UIKitCore 0x00007fff6dd17069 -[UIResponder _keyCommandForEvent:target:] + 182
14 UIKitCore 0x00007fff6dd17405 -[UIResponder _keyCommandForEvent:target:] + 1106
15 UIKitCore 0x00007fff6dd17405 -[UIResponder _keyCommandForEvent:target:] + 1106
16 UIKitCore 0x00007fff6dd17405 -[UIResponder _keyCommandForEvent:target:] + 1106
17 UIKitCore 0x00007fff6dd17405 -[UIResponder _keyCommandForEvent:target:] + 1106
18 UIKitCore 0x00007fff6dd17405 -[UIResponder _keyCommandForEvent:target:] + 1106
19 UIKitCore 0x00007fff6e0a36ce -[UIApplication(iOSMacSupport) _physicalKeyEvent:unmodified:shiftModified:commandModified:modifierFlags:isDown:timestampMachAbs:] + 517
20 UIKitCore 0x00007fff6e0a3803 -[UIApplication(iOSMacSupport) _sendKeyEvent:unmodified:shiftModified:commandModified:modifierFlags:isDown:timestampMachAbs:] + 116
21 UIKitCore 0x00007fff6e0a1c38 __58-[UIApplication(iOSMacSupport) _initiateIOSMacConnections]_block_invoke_3.396 + 124
22 UIKitMacHelper 0x00007fff602f4e3c -[UINSInputView _sendKeyEvent:isDown:] + 452
23 UIKitMacHelper 0x00007fff602f4ebd -[UINSInputView keyUp:] + 47
24 AppKit 0x00007fff2c675b98 -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 6512
25 AppKit 0x00007fff2c674005 -[NSWindow(NSEventRouting) sendEvent:] + 349
26 AppKit 0x00007fff2c672ccf -[NSApplication(NSEvent) sendEvent:] + 2739
27 AppKit 0x00007fff2c4be0cf -[NSApplication run] + 707
28 AppKit 0x00007fff2c490465 NSApplicationMain + 777
29 AppKit 0x00007fff2c7b369c _NSApplicationMainWithInfoDictionary + 16
30 UIKitMacHelper 0x00007fff602de8f1 UINSApplicationMain + 322
31 UIKitCore 0x00007fff6d186273 UIApplicationMain + 2105
32 Myapp 0x000000010010b39b main + 75
33 libdyld.dylib 0x00007fff669ad7fd start + 1
)
2020-02-15 14:08:30.338067+0100 Myapp[71888:2189305] *** Assertion failure in -[NSApplication _commonBeginModalSessionForWindow:relativeToWindow:modalDelegate:didEndSelector:contextInfo:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1894.30.142/AppKit.subproj/NSApplication.m:3694
2020-02-15 14:08:30.338318+0100 Myapp[71888:2189305] [General] Modal session requires modal window
2020-02-15 14:08:30.348833+0100 Myapp[71888:2189305] [General] (
0 CoreFoundation 0x00007fff2f3858ab __exceptionPreprocess + 250
1 libobjc.A.dylib 0x00007fff6563f805 objc_exception_throw + 48
2 CoreFoundation 0x00007fff2f3aed10 +[NSException raise:format:arguments:] + 88
3 Foundation 0x00007fff31aa7241 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
4 AppKit 0x00007fff2c7cf9d0 -[NSApplication _commonBeginModalSessionForWindow:relativeToWindow:modalDelegate:didEndSelector:contextInfo:] + 1583
5 AppKit 0x00007fff2c7cf39b -[NSApplication beginModalSessionForWindow:] + 37
6 AppKit 0x00007fff2c7cf2fc __35-[NSApplication runModalForWindow:]_block_invoke_2 + 39
7 AppKit 0x00007fff2c7cf2c2 __35-[NSApplication runModalForWindow:]_block_invoke + 70
8 AppKit 0x00007fff2c7ceb4c _NSTryRunModal + 100
9 AppKit 0x00007fff2c7cea31 -[NSApplication runModalForWindow:] + 128
10 AppKit 0x00007fff2cd21be6 __82-[NSTouchBarCustomizationController toggleCustomizationPalette:forceControlStrip:]_block_invoke_4 + 148
11 AppKit 0x00007fff2cd244c7 ___NSRunLoopTimerCreateWithHandler_block_invoke + 34
12 CoreFoundation 0x00007fff2f324804 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
13 CoreFoundation 0x00007fff2f3243be __CFRunLoopDoTimer + 859
14 CoreFoundation 0x00007fff2f323e9e __CFRunLoopDoTimers + 317
15 CoreFoundation 0x00007fff2f308aed __CFRunLoopRun + 2213
16 CoreFoundation 0x00007fff2f307bd3 CFRunLoopRunSpecific + 499
17 HIToolbox 0x00007fff2de5d65d RunCurrentEventLoopInMode + 292
18 HIToolbox 0x00007fff2de5d2a9 ReceiveNextEventCommon + 356
19 HIToolbox 0x00007fff2de5d127 _BlockUntilNextEventMatchingListInModeWithFilter + 64
20 AppKit 0x00007fff2c4cdba4 _DPSNextEvent + 990
21 AppKit 0x00007fff2c4cc380 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1352
22 AppKit 0x00007fff2c4be09e -[NSApplication run] + 658
23 AppKit 0x00007fff2c490465 NSApplicationMain + 777
24 AppKit 0x00007fff2c7b369c _NSApplicationMainWithInfoDictionary + 16
25 UIKitMacHelper 0x00007fff602de8f1 UINSApplicationMain + 322
26 UIKitCore 0x00007fff6d186273 UIApplicationMain + 2105
27 Myapp 0x000000010010b39b main + 75
28 libdyld.dylib 0x00007fff669ad7fd start + 1
)
2020-02-15 14:08:31.815095+0100 Myapp[71888:2189305] [AXRuntimeCommon] Unknown client: Myapp
But then the customize TouchBar action shows up in the menu (all my custom ones are gone), the ❗MISSING LABEL❗
issue is easily fixed.
So I wondered whether it was my menu customization code causing the error, but uncommenting the entire override func buildMenu(with builder: UIMenuBuilder)
in my AppDelegate does not cause the customization menu to appear – only the shortcut collision does.
In my Swift Catalyst app (macOS 10.15.3, Xcode 11.3.1), I am able to call
NSTouchBar.isAutomaticCustomizeTouchBarMenuItemEnabled = true
in my override func makeTouchBar()
method, and the "Customize Touch Bar..." menu item appears in the "View" menu tab. For the missing labels, the following works:
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if identifier == yourIdentifier {
let item = NSPickerTouchBarItem(...)
item.customizationLabel = "View Segmented Control"
return item
}
return nil
}