3. Serving Two Masters
Before we use this new controller from DudelViewController, we need to make two subclasses of the SelectColorController class, since DudelViewController uses the class of the currently active popover controller to determine exactly which controller it's dealing with.
NOTE
Purists may object to the
creation of subclasses that don't have any behavior or data of their
own, but then again, purists object to a lot of things. Being
reality-based, we'll do it this way, both because it's simple and
because it lets DudelViewController deal with all the popovers as consistently as possible.
Use the New File Assistant to create a new class. The assistant doesn't know about the SelectColorController class, so we need to use NSObject as the superclass and change it later. Name the class StrokeColorController, and give its .h and .m files the following contents:
// StrokeColorController.h
#import <Foundation/Foundation.h>
#import "SelectColorController.h"
@interface StrokeColorController : SelectColorController {}
@end
// StrokeColorController.m
#import "StrokeColorController.h"
@implementation StrokeColorController
@end
Create another new class named FillColorController, and define it like this:
// FillColorController.h
#import "SelectColorController.h"
@interface FillColorController : SelectColorController {}
@end
// FillColorController.m
#import "FillColorController.h"
@implementation FillColorController
@end
Now let's add support for both of these to DudelViewController in one fell swoop. Open DudelViewController.m, and start off adding these includes:
#import "StrokeColorController.h"
#import "FillColorController.h"
#import "ColorGrid.h"
Next, let's look at the code
that will launch each of the font selectors. The two action methods are
very similar, so instead of repeating a lot of code, we put most of it
into a separate method, shown here:
// both of the color popover action methods call this method.
- (void)doPopoverSelectColorController:(SelectColorController*)scc sender:(id)sender {
[self setupNewPopoverControllerForViewController:scc];
scc.container = self.currentPopover;
self.currentPopover.popoverContentSize = scc.view.frame.size;
// these have to be set after the view is already loaded (which happened
// a couple of lines ago, thanks to scc.view...)
scc.colorGrid.columnCount = 3;
scc.colorGrid.rowCount = 4;
scc.colorGrid.colors = [NSArray arrayWithObjects:
[UIColor redColor],
[UIColor greenColor],
[UIColor blueColor],
[UIColor cyanColor],
[UIColor yellowColor],
[UIColor magentaColor],
[UIColor orangeColor],
[UIColor purpleColor],
[UIColor brownColor],
[UIColor whiteColor],
[UIColor lightGrayColor],
[UIColor blackColor],
nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(colorSelectionDone:) name:ColorSelectionDone object:scc];
[self.currentPopover presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
- (IBAction)popoverStrokeColor:(id)sender {
StrokeColorController *scc = [[[StrokeColorController alloc]
initWithNibName:@"SelectColorController" bundle:nil] autorelease];
scc.selectedColor = self.strokeColor;
[self doPopoverSelectColorController:scc sender:sender];
}
- (IBAction)popoverFillColor:(id)sender {
FillColorController *fcc = [[[FillColorController alloc]
initWithNibName:@"SelectColorController" bundle:nil] autorelease];
fcc.selectedColor = self.fillColor;
[self doPopoverSelectColorController:fcc sender:sender];
}
In each of those cases, our
main controller is set up to listen for notifications from the color
selector. Here's the method that will handle the notifications:
- (void)colorSelectionDone:(NSNotification *)notification {
SelectColorController *object = [notification object];
UIPopoverController *popoverController = object.container;
[popoverController dismissPopoverAnimated:YES];
[self handleDismissedPopoverController:popoverController];
}
Finally, we take care of the
main popover dismissal handler. Add the bold lines to the following
method, which will make us notice new values in the color selectors:
- (void)handleDismissedPopoverController:(UIPopoverController*)popoverController {
if ([popoverController.contentViewController isMemberOfClass:
[FontListController class]]) {
// this is the font list, grab the new selection
FontListController *flc = (FontListController *)
popoverController.contentViewController;
self.font = [UIFont fontWithName:flc.selectedFontName size:self.font.pointSize];
} else if ([popoverController.contentViewController isMemberOfClass:
[FontSizeController class]]) {
FontSizeController *fsc = (FontSizeController *)
popoverController.contentViewController;
self.font = fsc.font;
} else if ([popoverController.contentViewController isMemberOfClass:
[StrokeWidthController class]]) {
StrokeWidthController *swc = (StrokeWidthController *)
popoverController.contentViewController;
self.strokeWidth = swc.strokeWidth;
} else if ([popoverController.contentViewController isMemberOfClass:
[StrokeColorController class]]) {
StrokeColorController *scc = (StrokeColorController *)
popoverController.contentViewController;
self.strokeColor = scc.selectedColor;
} else if ([popoverController.contentViewController isMemberOfClass:
[FillColorController class]]) {
FillColorController *fcc = (FillColorController *)
popoverController.contentViewController;
self.fillColor = fcc.selectedColor;
}
self.currentPopover = nil;
}
You should now be able to build
and run your app, and use the new color popovers to define stroke and
fill colors for all of the tools.
With this functionality in
place, we have all we need for a bare-bones vector-drawing app. No one's
going to mistake this for Adobe Illustrator, but it's easy and
functional enough for people to use for simple creations. Figure 3 shows an example of what you can create with Dudel.