Area: AppKit / NSToolbar Type: Incorrect/Unexpected Behavior (Regression) Summary
On macOS 27 beta, when an NSToolbarItem uses a custom view (NSToolbarItem.view) containing interactive controls (e.g. NSButton), clicks on those controls are silently swallowed. Window-level hit-testing stops at the private NSToolbarItemViewer instead of descending into the item's view hierarchy, so the controls never receive mouseDown and their target/action never fires. The same code works correctly on macOS 26 and earlier.
This breaks the long-standing, documented view-based toolbar item pattern. Any app that places custom interactive controls inside toolbar items is affected.
Steps to Reproduce
Create a window with an NSToolbar. In toolbar(_:itemForItemIdentifier:willBeInsertedIntoToolbar:), return an NSToolbarItem whose view is a container NSView holding an NSButton with a target/action. Run on macOS 27 beta and click the button. Minimal repro (complete AppDelegate):
import Cocoa
@main class AppDelegate: NSObject, NSApplicationDelegate, NSToolbarDelegate { var window: NSWindow!
func applicationDidFinishLaunching(_ notification: Notification) {
window = NSWindow(contentRect: NSRect(x: 200, y: 200, width: 600, height: 400),
styleMask: [.titled, .closable, .resizable],
backing: .buffered, defer: false)
let toolbar = NSToolbar(identifier: "Main")
toolbar.delegate = self
window.toolbar = toolbar
window.makeKeyAndOrderFront(nil)
}
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier id: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
let item = NSToolbarItem(itemIdentifier: id)
let container = NSView(frame: NSRect(x: 0, y: 0, width: 120, height: 28))
let button = NSButton(title: "Click Me", target: self, action: #selector(clicked))
button.frame = container.bounds
container.addSubview(button)
item.view = container
return item
}
@objc func clicked() { NSLog("BUTTON CLICKED") } // never logged on macOS 27 beta
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
[NSToolbarItem.Identifier("CustomItem")]
}
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
toolbarDefaultItemIdentifiers(toolbar)
}
}
Expected Results:
Clicking the button highlights it and fires its action, as on macOS 26 and every earlier release: window hit-testing resolves to the NSButton inside the toolbar item's custom view.
Actual Results
The click is silently consumed. The button never receives mouseDown, its action never fires, and no toolbar item action is sent either (the item is view-based and has no target/action of its own).
Diagnostic Evidence
We instrumented hitTest(_:) throughout the hierarchy in our production app and observed, for a single leftMouseDown over the button:
The custom subview's own hitTest is invoked during the traversal and correctly returns the NSButton. However, hit-testing initiated from the window's frame view (NSThemeFrame) resolves to NSToolbarItemViewer itself — the subview's result is discarded on the way up. Observed hit path: NSToolbarItemViewer <- NSToolbarView <- NSTitlebarView <- NSTitlebarContainerView <- NSThemeFrame Consequently AppKit dispatches mouseDown to NSToolbarItemViewer, which does nothing for a view-based item, and the event is dropped. This looks like an extension of the macOS 26 beta issue where NSGlassContainerView inside NSToolbarView intercepted hit-testing over the title bar (FB18201935, developer forums thread 788928). On macOS 27 the interception now also applies inside NSToolbarItemViewer for view-based items.
Impact
All interactive controls hosted in view-based toolbar items are unusable: buttons, search fields, segmented controls, etc. In our production app (RingCentral Video, a video-conferencing client), every in-meeting title-bar control (meeting info, network quality, view switcher, report) stopped responding on macOS 27 beta. We have shipped an application-level workaround that re-dispatches the event to the control when window hit-testing terminates at an ancestor of it, but this requires every affected app to implement custom event routing around a private view's behavior.
FB24375035