| < Previous PageNext Page > |
Many of the objects you see in an application let you manipulate them to signal intent. These objects include buttons, checkboxes, sliders, table views, file-system browsers, and menus (including the application and pop-up menus). In Cocoa, similar architectures underlie the implementations of the two general kinds of choice-enabling objects: controls and menus. In these architectures, various types of objects, including NSView objects, work together to allow the indication of user choice or intent.
Control and Cell Architecture
Menu Characteristics and Architecture
Represented Objects
A control is a user-interface object that responds to a user event such as a mouse click by sending a message to another object in an application. Common types of controls are buttons, sliders, and text fields (which typically send a message when users press the Return key). Other, less obvious controls are table views, data browsers, and color wells.
A control is an instance of a subclass of the abstract NSControl. It generally manages one or more cells—instances of a subclass of NSCell, another abstract class. If you look at the Application Kit class hierarchy (Figure 1-9), you notice that NSControl, a subclass of NSView, is the root of a fairly large branch of control classes: NSButton, NSStepper, NSTextField, and so on. At an entirely different location in the hierarchy (under NSObject), NSCell is the start of a branch of cell classes, most of which correspond to a control class: NSButtonCell, NSStepperCell, NSTextFieldCell, and so on.
Note: Two control classes dispense with cells entirely: NSScroller and NSColorWell. Instances of these classes, like any control, enable the user to indicate a choice, but they behave like any non-control view in that they draw themselves and respond to user events without using cells.
Most controls in the Application Kit manage a single cell. In these cases, the control has its cell do almost all of the actual work; it forwards many of the messages sent to it to its cell. But some controls manage multiple cells. The Application Kit takes two general approaches to managing multiple cells within controls:
A single cell instance is used as a template for drawing. Whenever a control must draw a cell, it uses this instance to replicate each cell representation, varying only the content. The NSTableView and NSOutlineView classes take this approach when drawing the cells of table-view and outline-view columns.
An individual cell instance represents each cell region drawn on a control. The NSMatrix class takes this approach when it requests its cell instances to draw themselves. NSBrowser and NSForm objects function in a similar way: their cells are also individual instances.
An NSMatrix object (or matrix) can manage most cell types. It arranges them in grids of arbitrary dimensions. When you construct a matrix of cells in Interface Builder, the cells are all copies of a given cell prototype. However, you can programmatically set the cells to be instances of different NSCell subclasses. NSBrowser and NSForm controls are more limited in the types of cells they can manage.
A single-cell control may seem to be a redundant design, but the value here is with the cell, not the control. See “Rationale for the Control-Cell Architecture” for an explanation.
Controls are full-fledged NSView objects. They can be marked as needing display, they are responsible for drawing themselves, they can respond to user events, and they are in the responder chain. At runtime, a control initially behaves like any view. The Application Kit asks it to redraw itself by invoking the control's drawRect: method. If an event message is dispatched to the responder chain and the control implements the appropriate NSResponder method, the control may have an opportunity to handle the event. However, instead of drawing and handling events themselves, controls pass on these responsibilities to their cells. If the control has multiple cells, it also coordinates the behavior of its cells.
As illustrated in Figure 6-21, a control within its drawRect: method typically sends drawWithFrame:inView: to a cell, asking it to draw itself within the passed-in rectangle. Because focus is already locked on the control, the cell can draw itself using the surface of its "host" view. A control in an implementation of a mouseDown: event method sends a trackMouse:inRect:ofView:untilMouseUp: to the cell in which the event occurred. Typically, the cell tracks the mouse events within its border until either the mouse exits or the cell receives a mouseUp: event; then it responds appropriately. (Other event types are handled in a similar manner.)
When a cell draws itself, it must represent two aspects. The first is the general appearance of the cell, which is consistent among cells of the same class. These characteristics include cell shape, background color, style, and visual indications of cell state (for example, a checkbox for cell selection or gray text if the cell isn't enabled). The second aspect is the cell's content, which individuates the cell. For content, a cell usually has a title (a string), an image, or in some cases both title and image. The title can be in a specified font. (Some cells, like NSSliderCell, have custom content instead of image or title.)
As content (or part of its content) a cell has an object value and possibly a represented object. The object value must be an object that can be formatted as a string and thus displayed as the cell title—for example, an NSNumber object encapsulating a float value. A represented object, although not displayed, is associated with the cell; for example, a button with a title of "Red" could have an NSColor represented object. For more on object values and represented objects, see “Represented Objects”.
What also individuates most cells is the information they encapsulate for action messages. The target-action mechanism (discussed in “The Target-Action Mechanism” allows a control to send a message to a designated object when the user activates the control (by clicking a button or pressing Return in a text field, for example). The action is a selector that identifies the method to invoke; the target is the designated object. (The target can be nil, which tells the application to search the responder chain for an object that can handle the message; see “Responders and the Responder Chain”.) The abstract NSActionCell class defines the interface for storing and retrieving action selectors and references to target objects. Most NSCell subclasses inherit from NSActionCell.
The control-cell architecture in the Application Kit has deep historical roots, going back to the early days of NeXTSTEP. But the necessity for it might seem puzzling at first glance. Why have controls manage cells at all? Why can't controls do the required work themselves?
There's no reason why a control couldn't, and if you are designing a custom control, a cell-less control is certainly a valid approach. But the control-cell architecture brings some advantages with it:
The architecture extends the usefulness of controls. A control such as a table view or a matrix can efficiently manage many different types of cells without having to have specific knowledge of each type. A cell such as an NSButtonCell object might be designed to work with NSButton controls, but it can also work with matrix and table-view objects.
A cell is an abstraction that simplifies the management of different kinds of graphical objects on a view. It allows for a kind of plug-in design where a control can host different kinds of graphical objects, each with its own identity, including target and action information.
The control-cell architecture permits a tighter coupling between a control and its cells than between, say, a view and a collection of subviews. Subviews are largely autonomous within their superview; a control can better act as a coordinator of its cells. For example, in a matrix of radio buttons, the control ensures that only one of the buttons is on at any time.
The NSTableView model for drawing cell regions—reusing a handful of cell instances as "rubber stamps" for drawing—is efficient, especially for controls that must manage a potentially unbounded number of subregions.
Even in cases where it's not feasible to use a few cell instances as templates for drawing, cells often offer a performance advantage over subviews. Views are relatively heavyweight objects both in terms of memory and computation. For example, tracking and propagating invalid view regions through the view hierarchy can exact a significant cost.
There are also tradeoffs between the use of cells versus subviews for drawing subregions. In the absence of the view invalidation mechanism, the control must take responsibility for figuring out what each cell need to draw on itself. But because views are general purpose objects, a specialized control can often do the required calculations more efficiently.
Note: See Control and Cell Programming Topics for Cocoa for more information about controls, cells, and the architecture enabling them to work together.
In addition to controls and cells, users can signal their intentions to an application (as well as to the operating system itself) using menus. A menu is a list of tersely worded choices—or menu items—with potentially nested menus (called submenus). Users typically choose a menu item by clicking it (although other methods are supported); as a result, a command is issued that the active application or operating system acts upon.
Cocoa supports various kinds of menus. Primary among these are the application-specific menus, which in Cocoa are collectively known as the main menu. At runtime, the application-specific menus include theapplication menu (the one bearing the application name) and all the menus to the right of it up through the Help menu. The application-specific menus share the menu bar with the Apple menu and menu extras—the service-specific menus to the right of the application-specific menus. The Application Kit automatically creates and manages certain application-specific menus, such as the Services menu, the Font menu, the Windows menu, and the Help menu. Other types of menus that Cocoa applications can manage include pop-up menus, contextual menus, and dock menus.
Menus and especially menu items have a number of interesting characteristics. Both menus and menu items have a title; the titles of menus are the strings that appear in the menu bar. Menu items can additionally have an image that appears to the left of the title, or they can have an image instead of a title. The title can be an attributed string, allowing different fonts and even text attachments (which permits an image to appear anywhere in the content area of a menu item). Menu items can also have an assigned key called the key equivalent which, when pressed together with modifier keys (except for Shift), causes the same response as a mouse click. Menu items are enabled or disabled, or they can indicate an on or off state; a check-mark appears to the left of the title if the item is in an on state.
Menus and menu items in Cocoa are instances of the classes NSMenu and NSMenuItem, respectively. In a Cocoa application, menus (in a generic sense) are based on a simple design that gives NSMenu and NSMenuItem objects complementary roles. An NSMenu object manages and draws a collection of menu items one below another. It contains an array of NSMenuItem objects representing this collection. An NSMenuItem object encapsulates all the data that characterizes a menu item but does no drawing or event handling itself. An NSMenu object uses the data in each NSMenuItem to draw the menu item within the boundaries of the menu, to track the location of the menu item within the menu, and to send an action message to a target object when users choose the menu item. When drawing, an NSMenu object uses an NSMenuItem object's title and image; for tracking, it uses the item's index; for sending action messages, it uses the action selector and target object stored in an NSMenuItem object.
Pop-up menus make use of this basic menu architecture. However, since they appear within an application's user interface, they have an additional design. Before a user clicks it, a pop-up menu appears as a button-like object. This object is an instance of NSPopUpButton, which manages an NSPopUpButtonCell instance—in other words, the control-cell architecture is used for this initial presentation. The NSPopUpButtonCell object contains an NSMenu object along with its encapsulated NSMenuItem objects. When a user clicks a pop-up button, this embedded menu is asked to display itself.
The menu items in a menu can be validated for the current context and, if an item is not relevant to that context, it can be disabled. NSMenu includes an auto-enabling feature that performs this validation automatically. Before a menu is displayed, it searches the responder chain for an object that can respond to a menu item's action message; if it cannot find any such object, it disables the item. Applications can further refine menu validation by implementing the NSMenuValidation informal protocol.
Contextual menus are implemented in way that's similar to pop-up menus. You can attach an NSMenu object (along with its menu items) to a view using the NSResponder method setMenu:. This menu lists commands specific to the view (which can be validated according to context). When users Control-click or right-click in the view, the menu is displayed.
Further Reading: Application Menu and Pop-up List Programming Topics for Cocoa discusses Cocoa menus and menu items in more detail.
A cell and menu item can have a represented object, an object that is arbitrarily associated with it. The target of an action message can ask a clicked menu item or cell (sender) for its represented object; the target can then display the represented object, load it, or perform whatever operation is required with the fetched object. A cell or menu item allows clients to access a represented object, and it archives and restores it, but otherwise does not use it.
To understand how you might use represented objects, consider a couple of examples. A matrix contains cells for setting the background color of a text view. These cells have names such as "Light Blue", "Light Gray", "Pink," and so on; the represented object for each menu item is an NSColor object encapsulating the RGB components for that color. Your application might use that represented object as in Listing 6-1.
Listing 6-1 Using a represented object
- (void)changeColor:(id)sender { |
NSColor *repObj = [sender representedObject]; |
[textView setBackgroundColor:repObj]; // textView is an outlet |
} |
Another example would be a pop-up menu in an Info window that allows you to change the displayed pane of settings. Assigned as a represented object to each menu item is an NSView object containing the required set of text fields, controls, and so on.
A represented object is not the same thing as the object value of a control or cell. A represented object is arbitrarily associated while an object value is the value behind what the cell or control displays. For example, an object value for a text field that displays "05/23/2006" would be an NSDate or NSCalendarDate object representing that displayed value. A cell or control's formatter must be able to "understand" an object value. (A formatter is an NSFormatter object).
Represented objects are not strictly limited to cells and menu items. For example, NSRulerMarker objects can have represented objects. And you can design your own custom views to have represented objects.
| < Previous PageNext Page > |
© 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-12-20)
|