NSTableView Tab Between Columns and move to next row

Multi tool use
up vote
0
down vote
favorite
Use Case:
This is a data entry scenario where the user needs to modify the data in three columns of a view based NSTableView; qty, item number and price. The objective is to keep the users hands on the keyboard and diminish the need for mouse'ing around the table to perform edits.
Detailed UI:
When tabbing within a row, the next column should become selected and editable which is (can be) the default behavior of a NSTableView. However, when in the last column (price) is being edited and tabbing out, it should wrap to the next available row, column 0 (qty) and allow the user to continue editing.
Likewise, backtab should be supported within a row, and when in the first column (qty) and backtabbing it should wrap up the the prior row, last column (price). If they are on row 0, column 0, it should wrap down to the last row, last column.
So essentially the user should be able to navigate and edit any row, column with tab or backtab presses.
While there are several solutions, using keyDown, keyUp, notifications etc a lot of it is older ObjC Code and not very Swifty.
Typically questions should include some kind of code but since I have a solution that works, I am including it as an answer so hopefully it will help a future reader.
Question:
Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters.
swift macos
add a comment |
up vote
0
down vote
favorite
Use Case:
This is a data entry scenario where the user needs to modify the data in three columns of a view based NSTableView; qty, item number and price. The objective is to keep the users hands on the keyboard and diminish the need for mouse'ing around the table to perform edits.
Detailed UI:
When tabbing within a row, the next column should become selected and editable which is (can be) the default behavior of a NSTableView. However, when in the last column (price) is being edited and tabbing out, it should wrap to the next available row, column 0 (qty) and allow the user to continue editing.
Likewise, backtab should be supported within a row, and when in the first column (qty) and backtabbing it should wrap up the the prior row, last column (price). If they are on row 0, column 0, it should wrap down to the last row, last column.
So essentially the user should be able to navigate and edit any row, column with tab or backtab presses.
While there are several solutions, using keyDown, keyUp, notifications etc a lot of it is older ObjC Code and not very Swifty.
Typically questions should include some kind of code but since I have a solution that works, I am including it as an answer so hopefully it will help a future reader.
Question:
Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters.
swift macos
Is "Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters." still a question or is it a summary?
– Willeke
Nov 10 at 17:32
@Willeke Thanks for the comment. It's both actually. It took a considerable amount of research and time to craft a working solution so I thought I would share it with others but there could certainly be options that may provide shorter or cleaner code to achieve the same results. So it is a question. I try to keep on topic and follow the guide when answering or asking here on SO, so if it's not beneficial to others I can delete.
– Jay
Nov 10 at 21:13
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
Use Case:
This is a data entry scenario where the user needs to modify the data in three columns of a view based NSTableView; qty, item number and price. The objective is to keep the users hands on the keyboard and diminish the need for mouse'ing around the table to perform edits.
Detailed UI:
When tabbing within a row, the next column should become selected and editable which is (can be) the default behavior of a NSTableView. However, when in the last column (price) is being edited and tabbing out, it should wrap to the next available row, column 0 (qty) and allow the user to continue editing.
Likewise, backtab should be supported within a row, and when in the first column (qty) and backtabbing it should wrap up the the prior row, last column (price). If they are on row 0, column 0, it should wrap down to the last row, last column.
So essentially the user should be able to navigate and edit any row, column with tab or backtab presses.
While there are several solutions, using keyDown, keyUp, notifications etc a lot of it is older ObjC Code and not very Swifty.
Typically questions should include some kind of code but since I have a solution that works, I am including it as an answer so hopefully it will help a future reader.
Question:
Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters.
swift macos
Use Case:
This is a data entry scenario where the user needs to modify the data in three columns of a view based NSTableView; qty, item number and price. The objective is to keep the users hands on the keyboard and diminish the need for mouse'ing around the table to perform edits.
Detailed UI:
When tabbing within a row, the next column should become selected and editable which is (can be) the default behavior of a NSTableView. However, when in the last column (price) is being edited and tabbing out, it should wrap to the next available row, column 0 (qty) and allow the user to continue editing.
Likewise, backtab should be supported within a row, and when in the first column (qty) and backtabbing it should wrap up the the prior row, last column (price). If they are on row 0, column 0, it should wrap down to the last row, last column.
So essentially the user should be able to navigate and edit any row, column with tab or backtab presses.
While there are several solutions, using keyDown, keyUp, notifications etc a lot of it is older ObjC Code and not very Swifty.
Typically questions should include some kind of code but since I have a solution that works, I am including it as an answer so hopefully it will help a future reader.
Question:
Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters.
swift macos
swift macos
asked Nov 10 at 17:14
Jay
17.9k42848
17.9k42848
Is "Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters." still a question or is it a summary?
– Willeke
Nov 10 at 17:32
@Willeke Thanks for the comment. It's both actually. It took a considerable amount of research and time to craft a working solution so I thought I would share it with others but there could certainly be options that may provide shorter or cleaner code to achieve the same results. So it is a question. I try to keep on topic and follow the guide when answering or asking here on SO, so if it's not beneficial to others I can delete.
– Jay
Nov 10 at 21:13
add a comment |
Is "Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters." still a question or is it a summary?
– Willeke
Nov 10 at 17:32
@Willeke Thanks for the comment. It's both actually. It took a considerable amount of research and time to craft a working solution so I thought I would share it with others but there could certainly be options that may provide shorter or cleaner code to achieve the same results. So it is a question. I try to keep on topic and follow the guide when answering or asking here on SO, so if it's not beneficial to others I can delete.
– Jay
Nov 10 at 21:13
Is "Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters." still a question or is it a summary?
– Willeke
Nov 10 at 17:32
Is "Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters." still a question or is it a summary?
– Willeke
Nov 10 at 17:32
@Willeke Thanks for the comment. It's both actually. It took a considerable amount of research and time to craft a working solution so I thought I would share it with others but there could certainly be options that may provide shorter or cleaner code to achieve the same results. So it is a question. I try to keep on topic and follow the guide when answering or asking here on SO, so if it's not beneficial to others I can delete.
– Jay
Nov 10 at 21:13
@Willeke Thanks for the comment. It's both actually. It took a considerable amount of research and time to craft a working solution so I thought I would share it with others but there could certainly be options that may provide shorter or cleaner code to achieve the same results. So it is a question. I try to keep on topic and follow the guide when answering or asking here on SO, so if it's not beneficial to others I can delete.
– Jay
Nov 10 at 21:13
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
First is to subClass a view based NSTableView set set it up like this
class ItemTableView: NSTableView, NSTableViewDataSource, NSTableViewDelegate, NSTextFieldDelegate {
I have a tableView dataSource called transactionArray which holds the items to be displayed within the tableView. I also include the delegate methods
func numberOfRows(in tableView: NSTableView) -> Int
and
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
within the class so it's self contained.
Here's the function to handle tab navigation
// handle tab and backtab in an editable view based tableView subclass
// will also scroll the edited cell into view when tabbing into view that are outside the viewable area
//ref https://developer.apple.com/documentation/appkit/nscontroltexteditingdelegate/1428898-control
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool
print(#function)
//let whichControl = control //this is the tableView textField where the event
// happened. In this case it will only be the
// NSTableCellView located within this tableView
let whichSelector = commandSelector //this is the event; return, tab etc
//these are the keypresses we are interested in, tab, backtab, return/enter.
let tabSelector = #selector( insertTab(_:) )
//let returnSelector = #selector( insertNewline(_:) ) //use this if you need
//custom return/enter handling
let backtabSelector = #selector( insertBacktab(_:) )
//if the user hits tab, need to determine where they are. If it's in the last
// column, need to see if there is another row and if so, move to next
// row, col 0 and go into edit. If it's a backtab in the first column, need
// to wrap back to the last row, last col and edit
if whichSelector == tabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
let lastCol = self.tableColumns.count - 1
if col == lastCol //we tabbed forward in the last column
let lastRow = self.transactionArray.count - 1
var rowToEdit: Int!
if row < lastRow //if we are above the last row, go to the next row
rowToEdit = row + 1
else //if we are at the last row, last col, tab around to the first row, first col
rowToEdit = 0
self.editColumn(0, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
return true //tell the OS we handled the key binding
else
self.scrollColumnToVisible(col + 1)
else if whichSelector == backtabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
if col == 0 //we tabbed backward in the first column
let lastCol = self.tableColumns.count - 1
var rowToEdit: Int!
if row > 0 //and we are after row zero, back up a row and edit the last col
rowToEdit = row - 1
else // we are in row 0, col 0 so wrap forward to the last col, last row
rowToEdit = self.transactionArray.count - 1
self.editColumn(lastCol, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
self.scrollColumnToVisible(lastCol)
return true
else
self.scrollColumnToVisible(col - 1)
return false //let the OS handle the key binding
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
First is to subClass a view based NSTableView set set it up like this
class ItemTableView: NSTableView, NSTableViewDataSource, NSTableViewDelegate, NSTextFieldDelegate {
I have a tableView dataSource called transactionArray which holds the items to be displayed within the tableView. I also include the delegate methods
func numberOfRows(in tableView: NSTableView) -> Int
and
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
within the class so it's self contained.
Here's the function to handle tab navigation
// handle tab and backtab in an editable view based tableView subclass
// will also scroll the edited cell into view when tabbing into view that are outside the viewable area
//ref https://developer.apple.com/documentation/appkit/nscontroltexteditingdelegate/1428898-control
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool
print(#function)
//let whichControl = control //this is the tableView textField where the event
// happened. In this case it will only be the
// NSTableCellView located within this tableView
let whichSelector = commandSelector //this is the event; return, tab etc
//these are the keypresses we are interested in, tab, backtab, return/enter.
let tabSelector = #selector( insertTab(_:) )
//let returnSelector = #selector( insertNewline(_:) ) //use this if you need
//custom return/enter handling
let backtabSelector = #selector( insertBacktab(_:) )
//if the user hits tab, need to determine where they are. If it's in the last
// column, need to see if there is another row and if so, move to next
// row, col 0 and go into edit. If it's a backtab in the first column, need
// to wrap back to the last row, last col and edit
if whichSelector == tabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
let lastCol = self.tableColumns.count - 1
if col == lastCol //we tabbed forward in the last column
let lastRow = self.transactionArray.count - 1
var rowToEdit: Int!
if row < lastRow //if we are above the last row, go to the next row
rowToEdit = row + 1
else //if we are at the last row, last col, tab around to the first row, first col
rowToEdit = 0
self.editColumn(0, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
return true //tell the OS we handled the key binding
else
self.scrollColumnToVisible(col + 1)
else if whichSelector == backtabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
if col == 0 //we tabbed backward in the first column
let lastCol = self.tableColumns.count - 1
var rowToEdit: Int!
if row > 0 //and we are after row zero, back up a row and edit the last col
rowToEdit = row - 1
else // we are in row 0, col 0 so wrap forward to the last col, last row
rowToEdit = self.transactionArray.count - 1
self.editColumn(lastCol, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
self.scrollColumnToVisible(lastCol)
return true
else
self.scrollColumnToVisible(col - 1)
return false //let the OS handle the key binding
add a comment |
up vote
0
down vote
First is to subClass a view based NSTableView set set it up like this
class ItemTableView: NSTableView, NSTableViewDataSource, NSTableViewDelegate, NSTextFieldDelegate {
I have a tableView dataSource called transactionArray which holds the items to be displayed within the tableView. I also include the delegate methods
func numberOfRows(in tableView: NSTableView) -> Int
and
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
within the class so it's self contained.
Here's the function to handle tab navigation
// handle tab and backtab in an editable view based tableView subclass
// will also scroll the edited cell into view when tabbing into view that are outside the viewable area
//ref https://developer.apple.com/documentation/appkit/nscontroltexteditingdelegate/1428898-control
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool
print(#function)
//let whichControl = control //this is the tableView textField where the event
// happened. In this case it will only be the
// NSTableCellView located within this tableView
let whichSelector = commandSelector //this is the event; return, tab etc
//these are the keypresses we are interested in, tab, backtab, return/enter.
let tabSelector = #selector( insertTab(_:) )
//let returnSelector = #selector( insertNewline(_:) ) //use this if you need
//custom return/enter handling
let backtabSelector = #selector( insertBacktab(_:) )
//if the user hits tab, need to determine where they are. If it's in the last
// column, need to see if there is another row and if so, move to next
// row, col 0 and go into edit. If it's a backtab in the first column, need
// to wrap back to the last row, last col and edit
if whichSelector == tabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
let lastCol = self.tableColumns.count - 1
if col == lastCol //we tabbed forward in the last column
let lastRow = self.transactionArray.count - 1
var rowToEdit: Int!
if row < lastRow //if we are above the last row, go to the next row
rowToEdit = row + 1
else //if we are at the last row, last col, tab around to the first row, first col
rowToEdit = 0
self.editColumn(0, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
return true //tell the OS we handled the key binding
else
self.scrollColumnToVisible(col + 1)
else if whichSelector == backtabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
if col == 0 //we tabbed backward in the first column
let lastCol = self.tableColumns.count - 1
var rowToEdit: Int!
if row > 0 //and we are after row zero, back up a row and edit the last col
rowToEdit = row - 1
else // we are in row 0, col 0 so wrap forward to the last col, last row
rowToEdit = self.transactionArray.count - 1
self.editColumn(lastCol, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
self.scrollColumnToVisible(lastCol)
return true
else
self.scrollColumnToVisible(col - 1)
return false //let the OS handle the key binding
add a comment |
up vote
0
down vote
up vote
0
down vote
First is to subClass a view based NSTableView set set it up like this
class ItemTableView: NSTableView, NSTableViewDataSource, NSTableViewDelegate, NSTextFieldDelegate {
I have a tableView dataSource called transactionArray which holds the items to be displayed within the tableView. I also include the delegate methods
func numberOfRows(in tableView: NSTableView) -> Int
and
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
within the class so it's self contained.
Here's the function to handle tab navigation
// handle tab and backtab in an editable view based tableView subclass
// will also scroll the edited cell into view when tabbing into view that are outside the viewable area
//ref https://developer.apple.com/documentation/appkit/nscontroltexteditingdelegate/1428898-control
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool
print(#function)
//let whichControl = control //this is the tableView textField where the event
// happened. In this case it will only be the
// NSTableCellView located within this tableView
let whichSelector = commandSelector //this is the event; return, tab etc
//these are the keypresses we are interested in, tab, backtab, return/enter.
let tabSelector = #selector( insertTab(_:) )
//let returnSelector = #selector( insertNewline(_:) ) //use this if you need
//custom return/enter handling
let backtabSelector = #selector( insertBacktab(_:) )
//if the user hits tab, need to determine where they are. If it's in the last
// column, need to see if there is another row and if so, move to next
// row, col 0 and go into edit. If it's a backtab in the first column, need
// to wrap back to the last row, last col and edit
if whichSelector == tabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
let lastCol = self.tableColumns.count - 1
if col == lastCol //we tabbed forward in the last column
let lastRow = self.transactionArray.count - 1
var rowToEdit: Int!
if row < lastRow //if we are above the last row, go to the next row
rowToEdit = row + 1
else //if we are at the last row, last col, tab around to the first row, first col
rowToEdit = 0
self.editColumn(0, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
return true //tell the OS we handled the key binding
else
self.scrollColumnToVisible(col + 1)
else if whichSelector == backtabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
if col == 0 //we tabbed backward in the first column
let lastCol = self.tableColumns.count - 1
var rowToEdit: Int!
if row > 0 //and we are after row zero, back up a row and edit the last col
rowToEdit = row - 1
else // we are in row 0, col 0 so wrap forward to the last col, last row
rowToEdit = self.transactionArray.count - 1
self.editColumn(lastCol, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
self.scrollColumnToVisible(lastCol)
return true
else
self.scrollColumnToVisible(col - 1)
return false //let the OS handle the key binding
First is to subClass a view based NSTableView set set it up like this
class ItemTableView: NSTableView, NSTableViewDataSource, NSTableViewDelegate, NSTextFieldDelegate {
I have a tableView dataSource called transactionArray which holds the items to be displayed within the tableView. I also include the delegate methods
func numberOfRows(in tableView: NSTableView) -> Int
and
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
within the class so it's self contained.
Here's the function to handle tab navigation
// handle tab and backtab in an editable view based tableView subclass
// will also scroll the edited cell into view when tabbing into view that are outside the viewable area
//ref https://developer.apple.com/documentation/appkit/nscontroltexteditingdelegate/1428898-control
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool
print(#function)
//let whichControl = control //this is the tableView textField where the event
// happened. In this case it will only be the
// NSTableCellView located within this tableView
let whichSelector = commandSelector //this is the event; return, tab etc
//these are the keypresses we are interested in, tab, backtab, return/enter.
let tabSelector = #selector( insertTab(_:) )
//let returnSelector = #selector( insertNewline(_:) ) //use this if you need
//custom return/enter handling
let backtabSelector = #selector( insertBacktab(_:) )
//if the user hits tab, need to determine where they are. If it's in the last
// column, need to see if there is another row and if so, move to next
// row, col 0 and go into edit. If it's a backtab in the first column, need
// to wrap back to the last row, last col and edit
if whichSelector == tabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
let lastCol = self.tableColumns.count - 1
if col == lastCol //we tabbed forward in the last column
let lastRow = self.transactionArray.count - 1
var rowToEdit: Int!
if row < lastRow //if we are above the last row, go to the next row
rowToEdit = row + 1
else //if we are at the last row, last col, tab around to the first row, first col
rowToEdit = 0
self.editColumn(0, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
return true //tell the OS we handled the key binding
else
self.scrollColumnToVisible(col + 1)
else if whichSelector == backtabSelector
let row = self.row(for: textView)
let col = self.column(for: textView)
if col == 0 //we tabbed backward in the first column
let lastCol = self.tableColumns.count - 1
var rowToEdit: Int!
if row > 0 //and we are after row zero, back up a row and edit the last col
rowToEdit = row - 1
else // we are in row 0, col 0 so wrap forward to the last col, last row
rowToEdit = self.transactionArray.count - 1
self.editColumn(lastCol, row: rowToEdit, with: nil, select: true)
self.scrollRowToVisible(rowToEdit)
self.scrollColumnToVisible(lastCol)
return true
else
self.scrollColumnToVisible(col - 1)
return false //let the OS handle the key binding
answered Nov 10 at 17:14
Jay
17.9k42848
17.9k42848
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53241431%2fnstableview-tab-between-columns-and-move-to-next-row%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
iARY1I,49R dMl,Sjc12vUhSl 5E NOVJ0RBx
Is "Can you suggest a simpler solution of how to navigate a view based tableview based on the above parameters." still a question or is it a summary?
– Willeke
Nov 10 at 17:32
@Willeke Thanks for the comment. It's both actually. It took a considerable amount of research and time to craft a working solution so I thought I would share it with others but there could certainly be options that may provide shorter or cleaner code to achieve the same results. So it is a question. I try to keep on topic and follow the guide when answering or asking here on SO, so if it's not beneficial to others I can delete.
– Jay
Nov 10 at 21:13