Cocoa Programming for Mac OS X - Aaron Hillegass [40]
[_startButton setEnabled:NO];
[_tableView setEnabled:NO];
}
- (void)speechSynthesizer:(NSSpeechSynthesizer *)sender
didFinishSpeaking:(BOOL)complete
{
NSLog(@"complete = %d", complete);
[_stopButton setEnabled:NO];
[_startButton setEnabled:YES];
[_tableView setEnabled:YES];
}
Your users will want to see that the default voice is selected in table view when the application starts. Create a new method, awakeFromNib, and within it select the appropriate row and scroll to it, if necessary:
- (void)awakeFromNib
{
// When the table view appears on screen, the default voice
// should be selected
NSString *defaultVoice = [NSSpeechSynthesizer defaultVoice];
NSInteger defaultRow = [_voices indexOfObject:defaultVoice];
NSIndexSet *indices = [NSIndexSet indexSetWithIndex:defaultRow];
[_tableView selectRowIndexes:indices byExtendingSelection:NO];
[_tableView scrollRowToVisible:defaultRow];
}
Build and run the application. If the speech synthesizer is speaking, you will not be able to change the voice, as the table view should be disabled. If the speech synthesizer is not speaking, you should be able to change the voice.
Common Errors in Implementing a Delegate
There are two very common errors that people make when implementing a delegate:
• Misspelling the name of the method: The method will not be called, and you will not get any error or warning from the compiler. The best way to avoid this problem is to copy and paste the declaration of the method from the documentation or the header file.
• Forgetting to set the delegate outlet: You will not get any error or warning from the compiler if you make this error.
Many Objects Have Delegates
Delegation is a commonly used design pattern in Cocoa. Here are some of the classes in the AppKit framework having delegate outlets:
NSAlert
NSAnimation
NSApplication
NSBrowser
NSDatePicker
NSDrawer
NSFontManager
NSImage
NSLayoutManager
NSMatrix
NSMenu
NSPathControl
NSRuleEditor
NSSavePanel
NSSound
NSSpeechRecognizer
NSSpeechSynthesizer
NSSplitView
NSTabView
NSTableView
NSText
NSTextField
NSTextStorage
NSTextView
NSTokenField
NSToolbar
NSWindow
For the More Curious: How Delegates Work
The delegate doesn’t have to implement all the methods, but if the object does implement a delegate method, it will get called. In many languages, this sort of thing would be impossible. How is it achieved in Objective-C?
NSObject has the the following method:
- (BOOL)respondsToSelector:(SEL)aSelector
Because every object inherits (directly or indirectly) from NSObject, every object has this method. It returns YES if the object has a method called aSelector. Note that aSelector is a SEL, not an NSString.
Imagine for a moment that you are the engineer who has to write NSTableView. You are writing the code that will change the selection from one row to another. You think to yourself, “I should check with the delegate.” To do so, you add a snippet of code that looks like this:
// About to change to row "rowIndex"
// Set the default behavior
BOOL ok = YES;
// Check whether the delegate implements the method
if ([delegate respondsToSelector:
@selector(tableView:shouldSelectRow:)])
{
// Execute the method
ok = [delegate tableView:self shouldSelectRow:rowIndex];
}
// Use the return value
if (ok)
{
...actually change the selection...
}
Note that the delegate is sent the message only if it has implemented the method. If the delegate doesn’t implement the message, the default behavior happens. (In reality, the result from respondsToSelector: is usually cached by the object with the delegate outlet. This makes performance considerably faster than would be implied by the code.)
After writing this method, you would carefully make note of its existence in the documentation for your class.
If you wanted to see the checks for the existence of the delegate methods, you could override respondsToSelector: in your delegate object:
- (BOOL)respondsToSelector:(SEL)aSelector
{
NSString *methodName