Search code examples
objective-ccocoanstableviewnspanel

Can't select rows in an NSTableView in Service app


I'm literally going nuts.

I have a service app that opens a pretty simple NSPanel that consists of an NSTableView, a label, and three NSButton controls.

That's it.

The table view is view-based, and has four different NSTableCellView rows defined in IB.

The table code is pretty straight forward; my NSWindowController subclass is both the data source and the delegate. And for the most part, everything works perfectly:

  • the window opens
  • the expected data source methods (numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:) get called
  • the table gets populated with its views and section separators via tableView:isGroupRow: and tableView:viewForTableColumn:row:
  • empty selections are disabled, so the delegate immediately receives a tableView:shouldSelectRow: message, and the table automatically selects the first row
  • the table appears exactly as it should and the first row is selected

Everything looks perfect, but I can't select a different rows. Nothing I click on in the table changes the selection.

I have tried:

  • subclassing NSTableCellView and verified that it is receiving a hitTest: call
  • tried "hacking" the table cell view hitTest: so it always return nil
  • tried various combination of "refuses first responder", "enabled", "editable" properties on the control view inside the table cell view
  • tried deleting all of the control views. so the table cell views were empty
  • tried implementing tableView:selectionIndexesForProposedSelection: instead of tableView:shouldSelectRow:
  • changed the NSPanel into a regular NSWindow

Nothing seems to make any difference. Nothing I click on in the table changes the selection, and my table view delegate never receives another tableView:shouldSelectRow: call.

Note that all of the other (NSButton) controls in the window work just fine. I can click on any of them.

Update #1

Per the comment, I tried changing the product to a plain-old .app, but it makes no difference.

My next step was to fiddle some more with the delegate methods and have narrowed down the problem to this:

  • If I implement tableView:shouldSelectRow:, the table calls my delegate method twice (because I have have "empty selection" turned off, the table must initially determine which row select as a default, so I get two calls, one for row 0 (NO) and a second for row 1 (YES)). However, if I click in the table, I never receive another tableView:shouldSelectRow:.

  • If I remove the implementation of tableView:shouldSelectRow:, table selection magically starts to work. (Except for the fact that you can select group rows, which is worng.)

  • Also tried implementing tableView:selectionIndexesForProposedSelection: instead of tableView:shouldSelectRow:; same behavior

So it appears if I implement any of the delegate methods to determine which rows are selectable, I can't select any rows. sigh

Update #2

I punted and refactored the app so there is now a new view controller object dedicated to managing the table view (rather than overloading the window controller). All data model and delegate methods were moved to the new view controller.

Also (thinking there might be something weird with the table view in IB), I deleted the table view from the NIB and recreated it and all of its connections.

Unfortunately, neither of these changes made any difference. If the tableView:shouldSelectRow: is implemented, the table is unusable. Delete the method, and it works again.

Update #3

And it just get weirder: thinking I could "hack" the row selection problem, I implemented the table view delegate methods tableViewSelectionIsChanging: and tableViewSelectionDidChange:. Neither get called. Even if I remove tableView:shouldSelectRow:, allowing the table selection to work, no tableViewSelectionIsChanging: or tableViewSelectionDidChange: is ever received.

Yet, if I add observers for NSTableViewSelectionIsChangingNotification and NSTableViewSelectionDidChangeNotification, those are received.

Also note that the NSViewController subclass that is the table view's delegate and data source explicitly conforms to <NSTableViewDelegate> and <NSTableViewDataSource>, so there's shouldn't be any reason why the table view should be confused as to what delegate methods are implemented.


Solution

  • Yikes! Now this is embarrassing.

    So the problem was a weak reference(s) to the window controller.

    Here's what was happening: the window controller was being created, loaded, and the window was presented. During the initial presentation and display, everything worked (tableView:shouldSelectRow: et. al.) because the window and view controllers existed. But in some future event loop, ARC destroyed the window and view controllers, leaving only the window on the screen with a table view, and the weak references to its delegate and data source objects were now nil.

    Solution was to fix the window controller management so it keeps a strong reference to the window controller until the window closes.

    Sometimes it's the simplest things that trip you up...