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 -----------------
- 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)
- The Art of SEO : Content Optimization (part 1)
- iPad SDK : New Graphics Functionality - Introducing Dudel (part 2)
- iPad SDK : New Graphics Functionality - Introducing Dudel (part 1)
 
 
Most View
- SharePoint 2010 : Office 2010 Client Applications (part 4)
- BizTalk Server 2009 : Using queues within asynchronous scenarios (part 3)
- Windows 7 : Enhancing Your Browsing Security (part 3) - Changing a Zone’s Security Level
- Parallel Programming with Microsoft Visual Studio 2010 : Introduction to Parallel Programming - Software Patterns
- Exchange Server 2010 : Upgrading from and Coexisting with Exchange Server 2003 (part 2)
- jQuery 1.3 : Compact forms (part 4)
- Windows Phone 7: Adding a Contact
- iPad SDK : The Structure of Core Text
- Securing Exchange Server : Configure Message Hygiene Options (part 1) - Battle Unwanted Mail
- Windows Server 2008 : Using wbadmin (part 2) - Backing Up & Restoring Volumes with wbadmin
Top 10
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 3) - Configuring Recipient Filtering
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 2)
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 1)
- Implementing Edge Services for an Exchange Server 2007 Environment : Installing and Configuring the Edge Transport Server Components
- What's New in SharePoint 2013 (part 7) - BCS
- What's New in SharePoint 2013 (part 6) - SEARCH
- What's New in SharePoint 2013 (part 6) - WEB CONTENT MANAGEMENT
- What's New in SharePoint 2013 (part 5) - ENTERPRISE CONTENT MANAGEMENT
- What's New in SharePoint 2013 (part 4) - WORKFLOWS
- What's New in SharePoint 2013 (part 3) - REMOTE EVENTS