Tabbing between cells in view-based table views

Hello,


In cell-based table views (NSTableView and NSOutlineView), if you had editable text field cells, tabbing out of a text cell would automatically move the focus to the next editable cell. So, say you had a table composed of five rows and three columns. If you were editing row 4, column 2 and hit tab, the cell at row 4, column 3 would gain the focus (presuming it was editable; otherwise the next editable cell would get the focus). If you hit tab again, row 5, column 1 would get the focus. And if the cell at row 5, column 3 was being edited and you hit tab, focus would cycle up to row 1, column 1.


In view-based tables, however, this does not work as expected. In view-based tables, things work as follows:


1. You double-click to start editing a cell - e.g. row 4, column 2.


2. You hit tab and things work as expected to begin with.


3. However, when you reach the last cell in the table and hit tab again, focus returns to the table view and editing ends.


4. If you then hit tab again, the cell you initially double-clicked in step (1) - e.g. row 4, column 2 - starts editing again.


This seems like a very silly bug to me, and I have reported it as such (ID#22576209). Is there a way around this behaviour or something I'm missing, though? View-based tables have been around for a while now, and Mac users tend to like ways of doing things with the keyboard in my experience, so I would have imagined that this would have come up as an issue a lot, but can find very little on it relating to view-based tables.


I have found one way of fixing the issue: I can make the delegate of my table view the delegate of all of my text fields in the table, and I can then override -control:textView:doCommandBySelector: to trap -insertTab: and -insertBacktab: and search for the next or previous editable cell myself. This works fine, but it means that for every table I create (and I have a *lot* of tables and outline views in my rather large app) I have to remember to hook up all of the text fields with a delegate, and I have to add this extra code, which was never necessary with cell-based tables.


Has anyone come across this and found a more straightforward solution? (I thought I might be able to do it in a subclass, but of course, -textView:doCommandBySelector: isn't called on NSTableView for view-based tables.)


Thanks,

Keith

I also had this problem. Unexpectedly we discovered it works if using auto layout. Hope that helps someone.


Also unexpectedly, when not using auto layout, it works in a secondary monitor, just not the primary. That probably won't help anyone but is a rather sad fact.

I had this working on Yosemite and El Capitan. The tab key would move focus to the next text field (or control) in the row. Once the end of the row was reached, focus would move to the first control in the next row.


The application uses auto-layout and has setAutorecalculatesKeyViewLoop set to YES.


When running on macOS Sierra, tabbing no longer wraps to the next row. The behavior depends on which row is selected when I hit tab:


- With no row selected, focus moves from the last control in a row to the next view after the table view

- With focus in the slected row, tabbing keeps keyboard focus "trapped" in the current row

- With focus in a row othe than the selected one, tabbing from the last control jumps to the first control of the selected row.


Hope this is a bug and not an intentional change. Hope this get fixed soon

Yes, I noticed the tab behaviour is even worse on Sierra. I spent an hour this week overrding the standard behaviour in an NSTextField subclass for use in tables to get better behaviour. I've overridden -textDidEndEditing: looking for the tab or back tab text movements. Then I look up the ancestor chain for the table view, get the row and column for the text field and iterate over subsequent columns and rows looking for the next editable text field in there, giving it first responder status if I find one, otherwise falling back on default behaviour. It's not a particularly elegant solution, but at least it works and doesn't seem completely random like the built-in behaviour.

I don't see -textDidEndEditing: getting called on my view-based table view.


I did not feel like manually registering for -textDidEndEditing: notifications on all text fields. Moreover, I'd also like to support tabbing between controls other than text fields. This I had working by overriding -insertTab: and -insertBacktab:


I am currently working on a fix that patches the key-view loop in -[NSTableView layout]. I let the super implementation create the default key loop. Then I loop over all existing table row views. I trust the key-view loop within the rows. I follow that to the end and then set the next row view as nextKeyView to the last control in the current row.


When there is no next row or when -rowViewAtRow:makeIfNecessary: returns nil, I set the nextKeyView of the last control in to the horizontal scroller of the enclosing scroll view.


This works fine in my test project. It is not sufficient for my actual application. Here the system decides to recalculate the key-view on some keyDown events.

For now my fix is to also recalculate the key-view loop after the private -_primitiveSetDefaultNextKeyView: method was called.

I overrode -textDidEndEditing: in an NSTextField subclass, not in my NSTableView subclass. Then I just assign the NSTextField in Interface Builder my subclass for tables in which I want this behaviour. This approach would be more awkward if you have other controls that need to become first responder too, though, as you note, because it relies on checking for the cell view's -textField property.


I'm glad you've found a solution, though, even if it involves overriding a private method. I don't think there's a clean solution, unfortunately. It's annoying that tabbing is so broken.

I have a project, currently using autolayout on Sierra. Tabbing in text fields is not moving focus to the cell below. Guess I'd have ot manually do it.

Tabbing between cells in view-based table views
 
 
Q