MOBILE

iPad SDK : Popovers - The Stroke Width Popover

8/13/2011 3:26:28 PM
Next up is the popover for setting the stroke width. This one is pretty similar to the one for font size. We'll give the user a slider to drag back and forth for setting the width, as well as a preview. This time, the preview will draw a few lines and curves in a UIBezierPath just like the ones the user can make, clearly showing the result of the user's selection.

1. Paving the Way

Start by creating yet another UIViewController subclass, using the New File Assistant. Just like FontSizeController, this one should not be a subclass of UITableViewController, but it should have an .xib file and be targeted for iPad. Name it StrokeWidthController. After Xcode creates it, open StrokeWidthController.h and give it the following content:

//  StrokeWidthController.h
#import <UIKit/UIKit.h>
@class StrokeDemoView;
@interface StrokeWidthController : UIViewController {
IBOutlet UISlider *slider;
IBOutlet UILabel *label;
IBOutlet StrokeDemoView *strokeDemoView;
CGFloat strokeWidth;
}
@property (assign, nonatomic) CGFloat strokeWidth;
- (void)takeIntValueFrom:(id)sender;
@end

This class is pretty similar to FontSizeController. The main differences are that here, we're keeping track of a simple floating-point value for the width, and we're also referencing a new class we're about to create, called StrokeDemoView, which we'll use to display the preview of the selected stroke width.

Before we create the GUI, we also need to create the StrokeDemoView class. Using the New File Assistant once again, make a new UIView subclass and name it StrokeDemoView. Just creating the class in our project is all we need to do in order to make Interface Builder know about the class and let us use it in the GUI. We'll go back and fill in the actual content later.

2. Creating the GUI

To begin, open StrokeWidthController.xib in Interface Builder. Once again, you'll see that the UIView it contains is meant to take up an entire screen, which isn't what we want here either. Use the attribute inspector to set the view's Status Bar to Unspecified, and then use the size inspector to make its size 320 by 320.

Now use the Library to find the three classes that are needed for our GUI: UISlider, UILabel, and StrokeDemoView. Drag each of them to the view, laying them out as shown in Figure 1.

Figure 1. Creating the GUI for StrokeWidthController

You can't really tell from the figure, but the large, white rectangle filling most of the view is an instance of StrokeDemoView. For best results with our preview-drawing code, make this view 320 by 257, since the StrokeDemoView is going to have hard-coded locations for the lines and curves it draws. Here, I've once again given the entire view a light-gray background, to make the control area stand out from the preview a bit. Use the attribute inspector to give the slider a reasonable range by setting its minimum value to 1 and its maximum value to 20.

Make all the connections described in the header file, by control-dragging from File's Owner to each of the GUI components and making the connection, then control-dragging from the slider back to File's Owner and selecting the takeIntValueFrom: action. Now the basic GUI configuration is complete, so let's return to Xcode and make it work!

3. Previewing the Stroke Width with a Custom View

Now we're going to define the StrokeDemoView class. This class will be pretty simple. It defines a property called strokeWidth, which determines how it draws its path. Our controller will set this each time the user moves the slider. StrokeDemoView.h looks like this:

//  StrokeDemoView.h
#import <UIKit/UIKit.h>
@interface StrokeDemoView : UIView {
CGFloat strokeWidth;
UIBezierPath *drawPath;
}
@property (assign, nonatomic) CGFloat strokeWidth;
@end

The implementation is also pretty simple. It defines the path to draw when it's initialized, and implements the setStrokeWidth: method in order to mark itself as "dirty" by calling [self setNeedsDisplay], so the view is scheduled for redrawing. The drawRect: method simply draws the path. Here's the whole thing:

//  StrokeDemoView.m
#import "StrokeDemoView.h"
@implementation StrokeDemoView
@synthesize strokeWidth;
- (void)setStrokeWidth:(CGFloat)f {
strokeWidth = f;
drawPath.lineWidth = f;
[self setNeedsDisplay];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
drawPath = [[UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 145, 100)] retain];
[drawPath appendPath:
[UIBezierPath bezierPathWithOvalInRect:CGRectMake(165, 10, 145, 100)]];

[drawPath moveToPoint:CGPointMake(10, 120)];
[drawPath addLineToPoint:CGPointMake(310, 120)];

[drawPath moveToPoint:CGPointMake(110, 140)];
[drawPath addLineToPoint:CGPointMake(310, 200)];

[drawPath moveToPoint:CGPointMake(100, 180)];
[drawPath addLineToPoint:CGPointMake(310, 140)];

[drawPath moveToPoint:CGPointMake(90, 200)];
[drawPath addCurveToPoint:CGPointMake(300, 230)
controlPoint1:CGPointMake(0, 0)
controlPoint2:CGPointMake(-100, 300)];
}
return self;
}
- (void)dealloc {
[drawPath dealloc];
[super dealloc];
}
- (void)drawRect:(CGRect)rect {
[[UIColor blackColor] setStroke];
[drawPath stroke];
}
@end


Note that since this class is instantiated only from within an .xib file, and never directly in code, we implement initWithCoder: and not initWithFrame:. If we wanted to also be able to instantiate this class in code, we would need to implement the latter as well in order to create the path.

4. Implementing the Controller

Now that we have a working StrokeDemoView, our next move is to go back and implement StrokeWidthController. This class is quite simple, and quite similar to the FontSizeController we built earlier. Here's the entire content of StrokeWidthController.m:

//  StrokeWidthController.m
#import "StrokeWidthController.h"
#import "StrokeDemoView.h"
@implementation StrokeWidthController
@synthesize strokeWidth;
- (void)viewDidLoad {
[super viewDidLoad];
NSInteger i = self.strokeWidth;
strokeDemoView.strokeWidth = i;
label.text = [NSString stringWithFormat:@"%d", i];
slider.value = i;
}
-
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (void)takeIntValueFrom:(id)sender {
NSInteger i = ((UISlider *)sender).value;
self.strokeWidth = i;
strokeDemoView.strokeWidth = self.strokeWidth;
label.text = [NSString stringWithFormat:@"%d", i];
slider.value = self.strokeWidth;
}
@end


5. Making it Work

Now all we need to do is update our main controller to make it aware of the new popover. Open DudelViewController.m, and start with an import:

#import "StrokeWidthController.h"

Once again, we update the handleDismissedPopoverController: method, this time grabbing the new stroke width after completion:

- (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;
}
self.currentPopover = nil;
}

And finally, we implement the action that sets it in motion:

- (IBAction)popoverStrokeWidth:(id)sender {
StrokeWidthController *swc = [[[StrokeWidthController alloc] initWithNibName:nil
bundle:nil] autorelease];
swc.strokeWidth = self.strokeWidth;
[self setupNewPopoverControllerForViewController:swc];
self.currentPopover.popoverContentSize = swc.view.frame.size;
[self.currentPopover presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}


Build and run the app, and try out the new popover. Now you can finally see what that UIBezierPath we defined in the StrokeDemoView class looks like! Figure 2 shows the stroke width popover in action.

Figure 2. Setting a stroke width

Drag the slider back and forth, and the stroke width changes. As we discussed, there's no concept of an active selection in Dudel, so changing this affects only the stroke width of the next graphic you draw, leaving the existing graphics unchanged.

Other  
  •  iPad SDK : Popovers - The Font Size Popover
  •  Beginning Android 3 : The Input Method Framework - Tailored to Your Needs
  •  Beginning Android 3 : Working with Containers - Scrollwork
  •  Mobile Application Security : SMS Security - Protocol Attacks (part 2)
  •  Mobile Application Security : SMS Security - Protocol Attacks (part 1)
  •  Mobile Application Security : SMS Security - Overview of Short Message Service
  •  iPad SDK : Popovers - The Font Name Popover (part 2)
  •  iPad SDK : Popovers - The Font Name Popover (part 1)
  •  Beginning Android 3 : Working with Containers - Tabula Rasa
  •  Beginning Android 3 : Working with Containers - LinearLayout Example & The Box Model
  •  iPhone Application Development : Reading and Writing User Defaults (part 2) - Implementing System Settings
  •  iPhone Application Development : Reading and Writing User Defaults (part 1) - Creating Implicit Preferences
  •  - Mobile Application Security : SMS Security - Overview of Short Message Service
  •  - Mobile Application Security : Bluetooth Security - Bluetooth Security Features
  •  Integrating Your Application with Windows Phone 7
  •  Introducing Windows Phone 7 Photo Features (part 2) - Using a Chooser to Open Photos & Saving Photos to the Phone
  •  Introducing Windows Phone 7 Photo Features (part 1) - Using a Chooser to Take Photos
  •  Mobile Application Security : Bluetooth Security - Bluetooth Technical Architecture
  •  Mobile Application Security : Bluetooth Security - Overview of the Technology
  •  Windows Phone 7 Development : Push Notifications - Implementing Cloud Service to Track Push Notifications
  •  
    Most View
    Samsung GALAXY Beam - Beam Me Up, Sammy
    Brother HL-5450DN - Built For Speed
    Microsoft SharePoint 2010 Web Applications : Presentation Layer Overview - Ribbon (part 1)
    Acer Aspire V5-551G Review – Laptop Using AMD Trinity 4 Quad-Core Chip
    Preparing Your Windows 8 PC : Adding Devices in Windows 8 (part 1) - Viewing Installed Devices
    ASUS Orion Pro Gaming Headset - Jack of both trades
    Windows Vista : Scheduling with Windows Calendar (part 2) - Working with Multiple Calendars, Importing Calendar Files, Sharing Calendars
    SharePoint 2010 : Planning Your Security Model - Maintaining Your Security Model
    Downloading and Installing 3CX Phone System (part 3) - Checking the status of 3CX
    Macbook Pro: The Inner Beauty (Part 1)
    Top 10
    MiniX Neo X5 - A Fantastic Android TV Box
    Quick Cloud Collaboration Keeps Projects In Sync
    AOC I2367FH 23-inch LED Monitor
    How To Find And Follow The Best Backup And Password Strategies (Part 3)
    How To Find And Follow The Best Backup And Password Strategies (Part 2)
    How To Find And Follow The Best Backup And Password Strategies (Part 1)
    Too Many Passwords? Let’s Simplify & Secure Your Digital Life
    Amazon Kindle Fire HD 8.9in - Is It Still Beautiful?
    Office In The Cloud Leverage The Web
    Wacom Cintiq 24HD Touch - Pen-Enabled Display Plus Multi-Touch Gestures (Part 2)