Row Selection and User Actions in Table Views
Displaying a list of data in a table view is of little use if the user is unable to select the rows. This chapter describes the actions the user can take to select the data rows, how to select rows programmatically, how to retrieve which rows are selected, and the role of the delegate in row selection.
Getting the Current Row Selection
Getting the current table view selection is an oft performed action by an application. The NSTableView class provides several methods for querying the table view as to its currently selected rows: selectedRowIndexes, selectedRow, numberOfSelectedRows, and isRowSelected:.
The selectedRowIndexes is used most often and is recommended. It will provide the full and correct selection regardless of whether a single item is selected or multiple items are selected. The method returns an NSIndexSet containing the indexes of the selected rows.
The selectedRow method returns the index of only the last selected row or -1 if no row is selected. This is an easiest way to get the current selection if multiple selection is disabled.
The numberOfSelectedRows method returns the number of selected rows. It can be used as a shortcut to determine if any objects are selected when empty selection is permitted. Its value can also be used to determine if user interface items should be enabled or disabled if they depend upon multiple items being selected in the table view. For example, if the numberOfSelectedRows is greater than 1, then you can disable the portions of the user interface that are only relevant when a single item is selected.
The isRowSelected: method allows an application to query if a row at a specific index is selected. If it is the method returns YES, otherwise it returns NO.
Applications often need to iterate over the selected rows. This can be done by iterating over the contents of the selection returned by selectedRowIndexes or by using the NSIndexSet Block enumeration method enumerateIndexesUsingBlock:. An example of using both these techniques are discussed in the “Iterating Through Index Sets” in Collections Programming Topics.
What Happens As Users Change Row Selections?
Users can select multiple rows using the shift key for continuous selections or the command key for non-contiguous selections. See “Make User Input Convenient” in OS X Human Interface Guidelines for more information about user selection actions.
As the user selects rows using the mouse, delegate methods (if implemented) are informed of the changes in row selection. The delegate receives the following messages as changes in the selection occurs by the user. Delegates for table view’s that are managed by Cocoa bindings also receive these same messages and can act on them as required.
selectionShouldChangeInTableView:. Implementation of this method is optional, it is usually implemented when it is necessary to perform validation on editing of a row before allowing the row selection to change.For example, if the user is editing a row in the table view and the content of that row does not meet the required criteria, the delegate should return
NOfrom this method to prevent the user from changing the selection. If the action was denied it is this method’s responsibility to inform the user as to why the selection change was disallowed and what action they can take to rectify that situation. ReturningYESallows the selection to be changed which is the default behavior if the method is not implemented.tableViewSelectionIsChanging:. This method informs the delegate that the row selection is about to change due to interaction with the mouse. Keyboard selection does not send invoke this method. Implementation of this method by the delegate is optional.tableViewSelectionDidChange:. This method informs the delegate that the row selection has changed and that, effectively, the table view selection action is completed. It is sent to the delegate when the user releases the mouse button, whether selection is limited to a single row or multiple selection is enabled.
The tableViewSelectionIsChanging: and tableViewSelectionDidChange: messages are notifications. Each is passed an NSNotification object. Sending the notification instance an object message returns the table view relevant to the selection change.
A table view delegate can also implement the tableView:selectionIndexesForProposedSelection: delegate method to allow or disallow the selection of a specific set of rows in a table view. The method is passed the indexes of the rows that are proposed to be selected and then returns the rows that will actually be selected. This allows the user to selectively allow and disallow row selection as appropriate. For example, if the user clicks in an area of the table row that shouldn’t trigger selection this method can be used to prevent that selection from taking place.
The application should be prepared to receive these messages on a continuous basis and, as such, ensure that they are implemented in an optimal fashion.
Allowing Multiple and Empty Selection
A table view can be configured to allow a single row to be selected at once, multiple rows to be selected simultaneously, or to attempt to prevent there from being an empty selection (that a at least one row is always selected). These attributes can be configured in Interface Builder or programmatically.
Programmatically, the table view methods for enabling and disabling these options are set using the following methods: setAllowsMultipleSelection:, allowsMultipleSelection, setAllowsEmptySelection:, and allowsEmptySelection.
Selecting and Deselecting Rows Programmatically
The NSTableView class provides an instance method to select rows programmatically, selectRowIndexes:byExtendingSelection:.
The selectRowIndexes:byExtendingSelection: method expects an NSIndexSet containing the indexes (0 based) of the rows to be selected, the second parameter specifies whether the current selection should be extended. If YES, the specified row indexes are selected in addition to any previously selected rows. If the extending selection parameter is NO, then the selection is changed to the newly specified rows. When this method is called the delegate will only receive the tableViewSelectionDidChange: notification.
To deselect a row invoke deselectRow: passing the index of the row to deselect. When this method is called the delegate will only receive the tableViewSelectionDidChange: notification.
There are also two convenience methods that allow the selection and deselection of all the items in the table view. These are primarily intended to be connected to the user interface. The deselectAll: method will deselect all the selected rows, but only if allowsEmptySelection returns YES. Likewise, selectAll: will select all the table view rows, but only if allowsMultipleSelection returns YES. Unlike the other programmatic methods, these two methods will invoke selectionShouldChangeInTableView: on the delegate object, if implemented, followed by tableViewSelectionDidChange:. If either deselectAll: or selectAll: are invoked without the proper allows... setting, they are ignored.
Using Type Selection to Select Rows
To simplify navigation in tables or to allow the user to select items without using the keyboard, table view supports type selection. When enabled (by default it is disabled) when a table view is selected you can type the first few letters of an entry and the table view content will be searched for a matching row. Type selection can be enabled and disabled using the setAllowsTypeSelect: method passing YES or NO. The default is YES.
How type select figures out which rows to match against is determined by the delegate method tableView:typeSelectStringForTableColumn:row:. This delegate method returns a string which type select uses for matching.
When the tableView:typeSelectStringForTableColumn:row: method is not implemented by the delegate the behavior is to invoke preparedCellAtColumn:row: passing the column index and row and then returns the stringValue. This allows type select to work on the values in any column and row in the table view. To restrict type select return nil for columns that should be excluded. The following method implementation only allows type select to match values in the “name” column.
- (NSString *)tableView:(NSTableView *)tableView typeSelectStringForTableColumn:(NSTableColumn *)tableColumn |
row:(NSInteger)row |
{ |
if ([[tableColumn identifier] isEqualToString:@"name"]) |
{ |
NSUInteger tableColumnIndex=[[tableView tableColumns] indexOfObject:tableColumn]; |
return [[tableView preparedCellAtColumn:tableColumnIndex |
row:row] stringValue]; |
} |
return nil; |
} |
Implementing the delegate method tableView:nextTypeSelectMatchFromRow:toRow:forString: allows the delegate to further customize type selection to match only between a specific selection of rows. It is passed the current type selection string and is expect to compare the appropriate values within the selected rows and return the row to select or -1 if no row matches.
Finally, the tableView:shouldTypeSelectForEvent:withCurrentSearchString: method provides the delegate the option to allow or disallow type selection for a particular keyboard NSEvent. You’d implement this method to prevent certain characters from causing type selecting. For example, if the event contains the ‘space’ character and the search string parameter is nil, the appropriate action may be to show a QuickLook preview of the selected table view item rather than type selection. However, if the search string parameter is non-nil then the ‘space’ character would likely be used to extend the type selection.
© 2011 Apple Inc. All Rights Reserved. (Last updated: 2011-07-06)