Cocoa Programming for Mac OS X - Aaron Hillegass [47]
Figure 8.14. Make Delete Key Trigger Delete Button
Build and run your application. You should be able to create and delete Person objects. You should also be able to edit the attributes of the Person objects using the table view. Finally, you should be able to open multiple untitled documents. (No, you can’t save those documents to a file. Soon, Grasshopper.)
Key-Value Coding and nil
Note that our example contains very little code. You described what should be displayed in each of the columns in Interface Builder, but there is no code that calls the accessor methods of your Person class. How does this work? Key-value coding. Key-value coding makes generic, reusable classes like NSArrayController possible.
The key-value coding methods will automatically coerce the type for you. For example, when the user types in a new expected raise, the formatter creates an instance of NSNumber. The key-value coding method setValue:forKey: automatically converts that into a float before calling setExpectedRaise:. This behavior is extremely convenient.
There is, however, a problem with converting an NSDecimalNumber * into a float: Pointers can be nil, but floats cannot. If setValue:forKey: is passed a nil value that needs to be converted into a nonpointer type, it will call its own method:
- (void)setNilValueForKey:(NSString *)s
This method, as defined in NSObject, throws an exception. Thus, if the user left the Expected Raise field empty, your object would throw an exception. Typically, you will override setNilValueForKey: so that it sets the instance variable to a default value. In this case, you are going to override this method in your Person class and set expectedRaise to 0.0. Add the following method to Person.m:
- (void)setNilValueForKey:(NSString *)key
{
if ([key isEqual:@"expectedRaise"]) {
[self setExpectedRaise:0.0];
} else {
[super setNilValueForKey:key];
}
}
Add Sorting
While the application is running, click on the column headers and note that sorting works (badly). In particular, the compare: method is ordering the names. This compare: method is very strongly case sensitive. For example, Z will come before a. Let’s change the method used for sorting.
Open RMDocument.xib. You can set the sorting criteria in the Attributes Inspector for each column. Users will be able to choose on which attribute the data will be sorted, by clicking on the header of the column containing that attribute.
Select the column that displays personName. In the Inspector, set the sort key to be personName and the selector to be caseInsensitiveCompare:, as shown in Figure 8.15.
Figure 8.15. Sorting on personName
The caseInsensitiveCompare: method is part of NSString. For example, you might do this:
NSString *x = @"Piaggio";
NSString *y = @"Italjet";
NSComparisonResult result = [x caseInsensitiveCompare:y];
// Would x come first in the dictionary?
if (result == NSOrderedAscending) {
...
}
NSComparisonResult is just an integer. NSOrderedAscending is –1. NSOrderedSame is 0. NSOrderedDescending is 1.
Build and run your application. Click on the header of the column to sort the data. Click again to see the data in reverse order.
For the More Curious: Sorting without NSArrayController
In Chapter 6, you populated a table view by implementing the dataSource methods explicitly. You might have wondered then how you could implement this sorting behavior in your own application.
The information that you added to the columns of the table is packed into an array of NSSortDescriptor objects. A sort descriptor includes the key, a selector, and an indicator of whether data should be sorted into ascending or descending order. If you have an NSMutableArray of objects, you can use the following method to sort it:
- (void)sortUsingDescriptors:(NSArray *)sortDescriptors
An optional table-view dataSource method is triggered when the user clicks on the header of a column with a sort descriptor: