View-Based Table Views
View-based table views allow applications to use views as the cells within a table view. This allows for complex user interfaces that developers can design in a much simpler manner. Views are created in the table view’s delegate object either programmatically or by loading prototype cells that have been designed in Xcode’s Interface Builder editor.
It’s important to remember that even if you are using the more advanced Cocoa bindings features, it is crucial that you understand how a table view works programmatically. It provides a grounding on the capabilities of the table class and also allows you to take advantage of mixing bindings and programmatic capabilities.
What Does a View-Based Table View Look Like?
View-based table views can be simple tables containing only text and images to complex views using image, color selection, buttons, And progress indicators. Any NSView subclass can be used within a view-based cell, including your own custom subclasses.
The table shown in Figure 2-1 shows a simple table implementation that contains two columns: one populated with a column containing both an image and text and a second that simply shows a text field. Constructing this table view using view-based table views requires no subclassing in order to display the content, both view cells are instances of the
NSCellView class. You simply drag the stock classes from the Interface Builder Object library and set the values of the appropriate subviews. The implementation of this table is contained in the TableViewPlayground example project.
You can also design complex table views that display multiple views that respond to mouse events, display animations, are divided into groups, automatically change their layout when resized, and provide a very rich user experience. Much, if not all, of the complex table view shown on the left hand side of Figure 2-2 is made using standard Application Kit view objects that are subviews of a subclass of
NSTableCellView which is displayed for each cell in the table view. The implementation of this table is contained in the TableViewPlayground example project.
The interface of the table-view cell’s were designed and created in Interface Builder then loaded into the table view as cell prototypes and populated with the necessary data.
View-Based Table View Classes and Structure
Table views are made up of the following classes:
NSTableViewclass. This class declares methods that allow you to configure the appearance of the table view—for example, specifying the default height of rows or providing a headers that are used to describe the data within a column. Other methods give you access to the currently selected row as well as specific rows or cells. You can call other methods of table view to manage selections, scroll the table view, and insert or delete rows and sections.
NSTableColumnclass. The column class is responsible for managing the horizontal position and the width of the cells of the table. They can be configured to allow the columns to be resized, re-ordered, and the content sorted with the state (optionally) stored with the application. Each table column has an identifier associated with it. This identifier is key when working with view-based table views and is also extremely useful in cell-based table views. Identifiers are discussed in more detail in “Using Column and Cell Identifiers Effectively.”
NSTableHeaderViewclass. The header view class is responsible for the cell that does its actual work, the
NSTableHeaderCell. It is the header cell that draws the column name if it is visible as well as being responsible for drawing the header content itself, optional sort indicators, drawing highlighting, and more.
NSClipViewclasses. While not exactly a part of the table view, virtually all table views are displayed using the classes that make up the scroll view mechanism. Although, it is not necessary that they are. For example, the sidebar in the Finder is an example of a series of outline views (subclasses of table views) that are displayed without using the scrolling mechanism.
View-based table views rely heavily on two additional classes and subclasses thereof:
NSTableRowViewclass. A view-based table view consists of a collection of multiple
NSTableRowViewinstances, one for each visible row. The frame rectangle of the row view is the full width of the table view and the height of the row taking into account the
intercellSpacing. While the default implementation of view-based tables provide a standard
NSTableRowViewinstance, you are able to customize row views using the delegate method
tableView:rowViewForRow:. Row views are responsible for drawing selection, drag and drop feedback, highlighting, column dividers, and any additional custom indicators that may be required, including a custom background. Each
NSTableRowViewhas a collection of subview that contains each table column cell.
NSTableCellViewclass. Instances of this class are typically used as the individual cells within a table view. The default
NSTableCellViewclass has Interface Builder outlets for a text field and an image view. VoiceOver automatically uses the contents of the text field as the words that are spoken, giving your application basic accessibility capabilities without effort on your part. Applications can create subclasses to add additional properties or use entirely custom
NSViewinstances to create new cells, although it is more often the case that a subclass of
NSTableCellViewis used instead. Figure 2-3 shows the components of an
Figure 2-4 shows how the components come together to display a view-based table view.
Each row is an instance of
NSTableRowView. The blue rows are highlighted as if selected by the user. The red rows are rows that have been subclassed to draw in their background in custom manner. Empty or missing cells are shown in the table and are allowed within view-based table views.
Using Column and Cell Identifiers Effectively
Every column in a table view has an associated identifier string that is set in the Identity inspector of the Xcode Interface Builder editor. This string is a convenient and versatile way of referring to and retrieving individual columns in a table view.
Applications typically use context-based names for the identifier of a table column, such as “Artist” or “Name”, so that the columns can be easily retrieved and identified by other aspects of the application. For example, if your application allows the showing and hiding of table columns, it can easily identify the column that must be shown or hidden by using the identifier and the
tableColumnWithIdentifier: method. Because columns can be reordered, you are unable to rely on the index of the column as a reliable means of retrieving a column, this makes the identifier of the column even more important.
When using view-based tables column identifiers serve the essential purpose of relating the table column to a view instance that the column displays. When the NSTableView delegate method
tableView:viewForTableColumn:row: method attempts to locate the cell for the table column, it uses the columns identifier to locate the cell. When setting the identifier manually (rather than using the Automatic function) it is your responsibility you ensure that those values stay in sync if you change either one.
However, if your table view does not require complex table column management such as retrieving individual table columns, or showing and hiding of columns, you can take advantage of the Automatic function of the Identifier. By default the Identifier field is set to Automatic. This means that it will create a unique identifier for the table column, and it will also ensure that the view instance within that table column has the same identifier. Further, it keeps these identifiers in sync, removing that responsibility from the developer.
Row Reuse and the View Queue
View-based table views create and maintain a queue of used view cells that allow for an efficient reuse of previously created cells. The view cells that are currently visible are considered ‘in use’ as is the currently selected cell (even if it currently isn’t visible) as is a cell that has editing in progress, for example a text field being edited. Views that don’t fit those criteria are inserted into the reuse queue as they are no longer used.
As table view cells are instantiated from within the
NSTableView instance in the Interface Builder editor, each are treated specially, as if they are their own nib. Each view cell has an owner that you can set as the object that acts as the ownder of any connections or outlets in the table view cell that are connected to the File’s Owner in Interface Builder.
When a new view for a specific ID is required the process shown in Figure 2-5 is used to find and return the requested view.
By using the reuse queue the view-based table view is able to effectively use memory, as well as increase the speed with which view cells are retrieved.
© 2011 Apple Inc. All Rights Reserved. (Last updated: 2011-07-06)