Categories (Extending Classes)
Objective-C’s built-in capability to expand already-existing classes is one of its most powerful features. This behavioral expansion is called a “category.” Categories extend class functionality without subclassing. You choose a descriptive expansion name, build a header, and then implement the functionality in a method file. Categories add methods to existing classes even if you did not define that class in the first place and do not have the source code for that class.
To build a category, you declare a new interface. Specify the category name (it’s arbitrary) within parentheses, as you see here. List any new public methods and properties and save the header file. This Orientation category expands the UIDevice class, which is the SDK class responsible for reporting device characteristics, including orientation, battery level, and the proximity sensor state. This interface adds a single property to UIDevice, returning a read-only Boolean value. The new isLandscape property reports back whether the device is currently using a landscape orientation.
@interface UIDevice (Orientation) @property (nonatomic, readonly) BOOL isLandscape; @end
You cannot add new instance variables to a category interface as you could when subclassing. You are instead expanding a class’s behavior, as shown in the source code of Listing 3-3. The code implements the landscape check by looking at the standard UIDevice orientation property.
You might use the new property like this:
NSLog(@"The device orientation is %@", [UIDevice currentDevice].isLandscape ? @"Landscape" : @"Portrait");
Here, the landscape orientation check integrates seamlessly into the SDK-provided UIDevice class via a property that did not exist prior to expanding the class. Just FYI, UIKit does offer a pair of device orientation macros (UIDeviceOrientationIsPortrait and UIDeviceOrientationIsLandscape), but you must pass these an orientation value, which you have to poll from the device.
Listing 3-3. Building an Orientation Category for the UIDevice Class
@interface UIDevice (Orientation) @property (nonatomic, readonly) BOOL isLandscape; @end @implementation UIDevice (Orientation) - (BOOL) isLandscape { return (self.orientation == UIDeviceOrientationLandscapeLeft) || (self.orientation == UIDeviceOrientationLandscapeRight); } @end