Action messages are typically sent by NSMenuItem objects or by NSControl objects. The latter objects usually work together with one or more NSCell objects. The cell object stores a method selector identifying the action message to be sent and a reference to the target object. (A menu item encapsulates its own action and target data.) When a menu item or control object is clicked or otherwise manipulated, it gets the action selector and target object—in the control’s case, from one of its cells—and sends the message to the target.
Note: For more on the target-action mechanism and action methods, see the appropriate sections in “Communicating With Objects" and “The Core Application Architecture“ in Cocoa Fundamentals Guide. Also, for information about untargeted action messages and the path they take up the responder chain, see “Action Messages” and “Responder Chain for Action Messages.”
You can set the action selector and target programmatically using, respectively, the methods setAction: and setTarget: (declared by NSActionCell, NSMenuItem, and other classes). However, you typically specify these in Interface Builder. In this application, you connect a control object to another object (the target) in the nib file by Control-dragging from the control object to the target and then selecting the action method of the target to invoke. If you want the action message to be untargeted, you can either set the target to nil programmatically or, in Interface Builder, make a connection between the menu item or control and the First Responder icon in the nib file window, as shown in Figure 3-1.
From Interface Builder you can generate a header file and implementation file for your Xcode project that include a declaration and skeletal implementation, respectively, for each action method defined for a class. These Interface Builder–defined methods have a return “value” of IBAction, which acts as a tag to indicated that the target-action connection is archived in a nib file. You can also add the declarations and skeletal implementations of action methods yourself; in this case, the return type is void.) The remaining required part of the signature is a single parameter typed as id and named (by convention) sender.
Listing 3-1 illustrates a straightforward implementation of an action method that toggles a clock’s AM-PM indicator when a user clicks a button.
Listing 3-1 Simple implementation of an action method
- (IBAction)toggleAmPm:(id)sender { |
[self incrementHour:12 andMinute: 0]; |
} |
Action methods, unlike NSResponder event methods, don’t have default implementations, so responder subclasses shouldn’t blindly forward action messages to super. The passing of action messages up the responder chain in the Application Kit is predicated merely on whether an object responds to the method, unlike with the passing of event messages. Of course, if you know that a superclass does in fact implement the method, you can pass it on up from your subclass, but otherwise don’t.
An important feature of action messages is that you can send messages back to sender to get further information or associated data. For example, the menu items in a given menu might have represented objects assigned to them; for example, a menu item with a title of “Red” might have a represented object that is an NSColor object. You can access this object by sending representedObject to sender.
You can take the messaging-back feature of action methods one step further by using it to dynamically change sender’s target, action, title, and similar attributes. Here is a simple test case: You want to control a progress indicator (an NSProgressIndicator object) with a single button; one click of the button starts the indicator and changes the button’s title to “Stop”, and then the next click stops the indicator and changes the title to back to “Start”. Listing 3-2 shows one way to do this.
Listing 3-2 Resetting target and action of sender—good implementation
- (IBAction)controlIndicator:(id)sender |
{ |
[[sender cell] setTarget:indicator]; // indicator is NSProgressIndicator |
if ( [[sender title] isEqualToString:@"Start"] ) { |
[[sender cell] setAction:@selector(startAnimation:)]; |
[sender setTitle:@"Stop"]; |
} else { |
[[sender cell] setAction:@selector(stopAnimation:)]; |
[sender setTitle:@"Start"]; |
} |
[[sender cell] performClick:self]; |
// set target and action back to what they were |
[[sender cell] setAction:@selector(controlIndicator:)]; |
[[sender cell] setTarget:self]; |
} |
However, this implementation requires that the target and action information be set back to what they were after the redirected action message is sent via performClick:. You could simplify this implementation by invoking directly sendAction:to:from:, the method used by the application object (NSApp) to dispatch action messages (see Listing 3-3).
Listing 3-3 Resetting target and action of sender—better implementation
- (IBAction)controlIndicator:(id)sender |
{ |
SEL theSelector; |
if ( [[sender title] isEqualToString:@"Start"] ) { |
theSelector = @selector(startAnimation:); |
[sender setTitle:@"Stop"]; |
} else { |
theSelector = @selector(stopAnimation:); |
[sender setTitle:@"Start"]; |
} |
[NSApp sendAction:theSelector to:indicator from:sender]; |
} |
In keyboard action messages, an action method is invoked as a result of a particular key-press being interpreted through the key bindings mechanism. Because such messages are so closely connected to specific key events, the implementation of the action method can get the event by sending currentEvent to NSApp and then query the NSEvent object for details. Listing 3-7 gives an example of this technique. See “The Path of Key Events” for a summary of keyboard action messages; also see “Key Bindings” for a description of that mechanism.
Last updated: 2007-03-16