After binding to NSBrowser indexPaths the browser assumes I'm using NSBrowserCell and calls unimplemented methods on my NSCell subclass

So after binding to NSBrowser selectionIndexPaths: https://developer.apple.com/library/archive/documentation/Cocoa/Reference/CocoaBindingsRef/BindingsText/NSBrowser.html

this causes NSBrowser to do some weird stuff that seems completely unrelated to this particular binding. It starts calling NSBrowserCell methods on my cells but my cell is not a NSBrowserCell. My cell is actually a subclass of NSTextFieldCell. But NSBrowser starts sending setIsLeaf: (which I don't implement).

In any case if I implement the -setLeaf: that solves that unrecognized selector, but now my cells don't draw titles.

Not sure why binding to selectionIndexPaths causes this behavior? The cell stuff seems unrelated to this particular binding. I am of course using the newer but still pretty old item based APIs... do these bindings only support using NSMatrix?

I do set the binding after calling -setCellClass: but makes no difference.

I also just tried overriding -setSelectionIndexPaths: but NSBrowser does not use the setter.

Hi Macho Man Randy Savage,

Your diagnosis is essentially right, and I dug deeper than I expected after reproducing it. The situation is worse than just one wrong selector — NSBrowser's Cocoa Bindings layer doesn't compose with the item-based delegate API at all, regardless of cell class.

I built a small test project (item-based NSBrowser, an NSTextFieldCell subclass installed as cellPrototype, and selectionIndexPaths bound to a property). With the binding in place, the app crashes during draw with:

*** -[NSTextFieldCell setLeaf:]: unrecognized selector sent to instance ...

The actual selector is setLeaf:, not setIsLeaf: (worth knowing if you grep for it). The stack tells the rest of the story:

0  CoreFoundation     __exceptionPreprocess
1  libobjc.A.dylib    objc_exception_throw
...
5  AppKit             -[NSBrowserBinder browser:willDisplayCell:atRow:column:] + 156
6  AppKit             -[_NSBindingAdaptor browser:willDisplayCell:atRow:column:] + 288
7  AppKit             -[NSBrowser _sendDelegateWillDisplayCell:atRow:column:] + 64

When you bind selectionIndexPaths, AppKit installs an _NSBindingAdaptor — specifically an NSBrowserBinder — that interposes itself between NSBrowser and your delegate. The binder's browser:willDisplayCell:atRow:column: strictly assumes the cell is an NSBrowserCell and sends setLeaf: to it during display, then forwards the call to the wrapped delegate.

Implementing -setLeaf: as a no-op (your attempted fix) gets past that crash, but two other things break:

  • The binder also expects the wrapped delegate to implement browser:willDisplayCell:atRow:column:. If yours doesn't, you'll get a second unrecognized-selector crash on the AppDelegate as the binder forwards through. (You may have implemented it without mentioning it, which is why you saw "titles missing" rather than another crash.)
  • More importantly, switching to an NSBrowserCell subclass — which I tried to verify as the alternative — also fails to draw titles. The bindings layer for NSBrowser predates the item-based API. It expects the legacy NSMatrix-based data flow where contentValueForPath supplies cell content. With the item-based delegate (browser:numberOfChildrenOfItem:, browser:objectValueForItem:, and so on) in play, the binder doesn't call those methods for cell content, and NSBrowserCell ends up with no value to draw.

So the issue isn't really "the binder hates non-NSBrowserCell cells." It's "the binder is older than the item-based API, and the two don't share a data-source model."

The cleanest fix is to drop the selectionIndexPaths binding and read selection through the delegate API instead. Wire your selection state through target/action, and call selectionIndexPath / selectionIndexPaths on the browser when you need the current value. That removes NSBrowserBinder from the call path — no setLeaf: calls, no forwarded willDisplayCell:, and your existing item-based delegate methods supply cell content normally.

If you really need bindings for some other architectural reason, the path that's likely to work is the legacy NSMatrix-based browser API with the contentValueForPath binding (and NSBrowserCell cells) — that's the configuration the bindings were originally designed for. I didn't verify this end-to-end, since most projects won't want to step that far back in API surface.

If you'd find Cocoa Bindings on NSBrowser's item-based API working with custom cells useful, please file a Feedback Report. The bindings adaptor is older than the item-based API; making it compose with the modern data flow is more of an API gap than a strict bug, and FBs from real-world cases like yours raise the priority on those gaps.

After binding to NSBrowser indexPaths the browser assumes I'm using NSBrowserCell and calls unimplemented methods on my NSCell subclass
 
 
Q