NSToolbarItemViewer no longer resolves hit-testing to a view-based NSToolbarItem's subviews — interactive controls in custom toolbar item views receive no mouse events (regression in macOS 27 beta)

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

Works fine here.

I think the problem lies somewhere in your hitTest logic. That does seem to be the problem you're having, but none of the example code that you posted does anything with hit testing.

In my case, I have a custom NSImageView specifically so I can get the location of the toolbar item. That is a little tricky. But your example is straightforward. I commented out my code and replaced it with your code and it printed the desired message.

What I do is set a custom callback in my NSImageView-derived class. When I setup the toolbar item, I assign a closure to his callback that calls my method, passing in the toolbar item itself.

Then, in the NSImageView subclass, I override mouseDown to call my custom "onClick" closure.

That closure calls my parent view method with the toolbar item as a parameter. From that, I can get the toolbar item view's bounds and use those to display a popover.

I think you're in a rabbit hole with the hitTest path. It seems irrelevant.

Buttons really aren't appropriate for the toolbar anyway. It sounds like you've got some other code that's doing something funky with NSView and that code is incompatible with life in the toolbar.

NSToolbarItemViewer no longer resolves hit-testing to a view-based NSToolbarItem's subviews — interactive controls in custom toolbar item views receive no mouse events (regression in macOS 27 beta)
 
 
Q