Programming4us
         
 
 
Programming

iPad SDK : New Graphics Functionality - We Are All Tool Users (part 4) - The Ellipse and Rectangle Tools

12/18/2010 4:15:09 PM

3. The Ellipse and Rectangle Tools

Next up are the Ellipse and Rectangle tools. They are extremely similar to one another, and also to the Line tool. From a user standpoint, they function similarly: you touch in one corner, drag, and release to define the opposite corner. The Rectangle tool creates a rectangle, and the Ellipse tool creates (you guessed it) an ellipse.

Make a new RectangleTool class, and give it this code:

//  RectangleTool.h
#import <Foundation/Foundation.h>
#import "Tool.h"
@interface RectangleTool : NSObject <Tool> {
id <ToolDelegate> delegate;
NSMutableArray *trackingTouches;
NSMutableArray *startPoints;
}
+ (RectangleTool *)sharedRectangleTool;
@end

// RectangleTool.m
#import "RectangleTool.h"

#import "PathDrawingInfo.h"
#import "SynthesizeSingleton.h"
@implementation RectangleTool
@synthesize delegate;
SYNTHESIZE_SINGLETON_FOR_CLASS(RectangleTool);
- init {
if ((self = [super init])) {
trackingTouches = [[NSMutableArray array] retain];
startPoints = [[NSMutableArray array] retain];
}
return self;
}
- (void)activate {
}
- (void)deactivate {
[trackingTouches removeAllObjects];
[startPoints removeAllObjects];
}

As you can see, like the LineTool class, this class maintains arrays of startingPoints and trackingTouches.

The "touches" methods are where the interesting work of this class is done. Like the Line tool, the Rectangle tool is capable of tracking multiple simultaneous touches, ultimately creating a new line for each of them.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UIView *touchedView = [delegate viewForUseWithTool:self];
for (UITouch *touch in [event allTouches]) {
// remember the touch, and its original start point, for future
[trackingTouches addObject:touch];
CGPoint location = [touch locationInView:touchedView];
[startPoints addObject:[NSValue valueWithCGPoint:location]];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UIView *touchedView = [delegate viewForUseWithTool:self];
for (UITouch *touch in [event allTouches]) {
// make a rect from the start point to the current point
NSUInteger touchIndex = [trackingTouches indexOfObject:touch];
// only if we actually remember the start of this touch...
if (touchIndex != NSNotFound) {
CGPoint startPoint = [[startPoints objectAtIndex:touchIndex] CGPointValue];
CGPoint endPoint = [touch locationInView:touchedView];
CGRect rect = CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:rect];
PathDrawingInfo *info = [PathDrawingInfo pathDrawingInfoWithPath:path fillColor:delegate.fillColor strokeColor:delegate.strokeColor];
[delegate addDrawable:info];
[trackingTouches removeObjectAtIndex:touchIndex];
[startPoints removeObjectAtIndex:touchIndex];
}
}
}


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}

The following method draws the current state of the rectangle while you are still dragging it around. Only later does the object being drawn here get added to the view's list of drawable items.

- (void)drawTemporary {
UIView *touchedView = [delegate viewForUseWithTool:self];
for (int i = 0; i<[trackingTouches count]; i++) {
UITouch *touch = [trackingTouches objectAtIndex:i];
CGPoint startPoint = [[startPoints objectAtIndex:i] CGPointValue];
CGPoint endPoint = [touch locationInView:touchedView];
CGRect rect = CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:rect];
[delegate.fillColor setFill];
[path fill];
[delegate.strokeColor setStroke];
[path stroke];
}
}
- (void)dealloc {
[trackingTouches release];
[startPoints release];
self.delegate = nil;
[super dealloc];
}
@end


Now for the Ellipse tool. Its only substantial difference from the Rectangle tool is the creation of UIBezierPaths in touchesEnded:withEvent: and drawTemporary.

//  EllipseTool.h
#import <Foundation/Foundation.h>
#import "Tool.h"
@interface EllipseTool : NSObject <Tool> {
id <ToolDelegate> delegate;
NSMutableArray *trackingTouches;
NSMutableArray *startPoints;
}
+ (EllipseTool *)sharedEllipseTool;
@end


// EllipseTool.m
#import "EllipseTool.h"
#import "PathDrawingInfo.h"
#import "SynthesizeSingleton.h"
@implementation EllipseTool
@synthesize delegate;
SYNTHESIZE_SINGLETON_FOR_CLASS(EllipseTool);
- init {
if ((self = [super init])) {
trackingTouches = [[NSMutableArray arrayWithCapacity:100] retain];
startPoints = [[NSMutableArray arrayWithCapacity:100] retain];
}
return self;



}
- (void)activate {
}
- (void)deactivate {
[trackingTouches removeAllObjects];
[startPoints removeAllObjects];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UIView *touchedView = [delegate viewForUseWithTool:self];
for (UITouch *touch in [event allTouches]) {
// remember the touch, and its original start point, for future
[trackingTouches addObject:touch];
CGPoint location = [touch locationInView:touchedView];
[startPoints addObject:[NSValue valueWithCGPoint:location]];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UIView *touchedView = [delegate viewForUseWithTool:self];
for (UITouch *touch in [event allTouches]) {
// make an ellipse/oval from the start point to the current point
NSUInteger touchIndex = [trackingTouches indexOfObject:touch];
// only if we actually remember the start of this touch...
if (touchIndex != NSNotFound) {
CGPoint startPoint = [[startPoints objectAtIndex:touchIndex] CGPointValue];
CGPoint endPoint = [touch locationInView:touchedView];
CGRect rect = CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
PathDrawingInfo *info = [PathDrawingInfo pathDrawingInfoWithPath:path fillColor:delegate.fillColor strokeColor:delegate.strokeColor];
[delegate addDrawable:info];
[trackingTouches removeObjectAtIndex:touchIndex];
[startPoints removeObjectAtIndex:touchIndex];
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)drawTemporary {
UIView *touchedView = [delegate viewForUseWithTool:self];
for (int i = 0; i<[trackingTouches count]; i++) {
UITouch *touch = [trackingTouches objectAtIndex:i];
CGPoint startPoint = [[startPoints objectAtIndex:i] CGPointValue];
CGPoint endPoint = [touch locationInView:touchedView];
CGRect rect = CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
[delegate.fillColor setFill];
[path fill];
[delegate.strokeColor setStroke];
[path stroke];
}
}
- (void)dealloc {
[trackingTouches release];


[startPoints release];
self.delegate = nil;
[super dealloc];
}
@end

Here are the necessary changes to DudelViewController.m:

#import "RectangleTool.h"
#import "EllipseTool.h"

- (IBAction)touchEllipseItem:(id)sender {
self.currentTool = [EllipseTool sharedEllipseTool];
[ellipseButton setImage:[UIImage imageNamed:@"button_ellipse_selected.png"]];
}
- (IBAction)touchRectangleItem:(id)sender {
self.currentTool = [RectangleTool sharedRectangleTool];
[rectangleButton setImage:[UIImage imageNamed:@"button_rectangle_selected.png"]];
}


With those in place, the next two buttons at the bottom of the GUI should now be working. Figure 3 shows some of the kinds of shapes that can be created with these tools. As with the previous tools, these also work with multitouch, so you should be able to drag multiple fingers at once to create several rectangles or ellipses simultaneously.

Figure 3. Overlapping blocks and curves
Other -----------------
- iPad SDK : New Graphics Functionality - We Are All Tool Users (part 3) - The Line Tool
- iPad SDK : New Graphics Functionality - We Are All Tool Users (part 2) - The Pencil Tool
- iPad SDK : New Graphics Functionality - We Are All Tool Users (part 1)
- Security-As-a-[Cloud] Service : Today’s Offerings
- CSS for Mobile Browsers : CSS Sprites
- CSS for Mobile Browsers : Common Patterns (part 4)
- CSS for Mobile Browsers : Common Patterns (part 3) - Titles and Pseudoclasses
- CSS for Mobile Browsers : Common Patterns (part 2) - Rounded corners
- CSS for Mobile Browsers : Common Patterns (part 1) - Absolute and floating positions
- iPad SDK : New Graphics Functionality - The Basic Drawing Architecture
- jQuery 1.3 : Compact forms (part 6)
- jQuery 1.3 : Compact forms (part 5)
- jQuery 1.3 : Compact forms (part 4)
- jQuery 1.3 : Compact forms (part 3)
- jQuery 1.3 : Compact forms (part 2) - AJAX auto-completion
- jQuery 1.3 : Compact forms (part 1) - Placeholder text for fields
- The Art of SEO : Duplicate Content Issues (part 3)
- The Art of SEO : Duplicate Content Issues (part 2) - How Search Engines Identify Duplicate Content
- The Art of SEO : Duplicate Content Issues (part 1) - Consequences of Duplicate Content
- The Art of SEO : Content Optimization (part 2)
 
 
Most View
- Coding JavaScript for Mobile Browsers (part 12) - Swipe gesture
- Microsoft Exchange Server 2003: Configuring Recipient Objects (part 8) - Moving Mailboxes with the Microsoft Exchange Mailbox Merge Wizard
- Windows 7 : Working with the Command-Line Tools (part 1) - Working with Disk Management Tools
- BizTalk Server 2009 : The core principles of a service-oriented architecture (part 3)
- Working with the SharePoint 2010 Management Shell (part 5) - Using Verbs
- Coding JavaScript for Mobile Browsers (part 6)
- SQL Injection Attacks and Defense : Accessing the File System (part 2) - Writing Files
- Windows Server 2008 : Configuring IPv4 and IPv6 Addressing
- Programming Windows Phone 7: Orientation Events
- Windows Phone 7 : Getting Real-Time Traffic Conditions
Top 10
- SQL Server 2008 High Availability : Building Solutions with One or More HA Options
- SQL Azure : Design Patterns (part 2) - Sharding
- SQL Server 2008: Administering Database Objects - Working with Database Snapshots
- SharePoint 2010 : Create an Event with a Website (part 1)
- Windows Azure Storage : REST API (part 1)
- SOA with .NET and Windows Azure : Windows Workflow Foundation (part 1) - WF Architecture
- LINQ to Objects : Writing Basic Queries - Query Syntax Style Options
- SQL Azure Backup Strategies (part 1) - Copying a Database
- Coding JavaScript for Mobile Browsers (part 1) - Code Execution
- Developing for Windows Phone and Xbox Live : Multiplayer Games (part 4) - Building a Game Lobby