Optimizing the QuartzFun Application

Một phần của tài liệu beginning iphone 3 development exploring the iphone sdk phần 8 ppt (Trang 41 - 45)

Our application does what we want, but we should consider a bit of optimization. In our application, you won’t notice a slowdown, but in a more complex application, running on a slower processor, you might see some lag. The problem occurs in QuartzFunView.m, in the methods touchesMoved: and touchesEnded:. Both methods include this line of code:

[self setNeedsDisplay];

Obviously, this is how we tell our view that something has changed, and it needs to redraw itself. This code works, but it causes the entire view to get erased and redrawn, even if only a tiny little bit changed. We do want to erase the screen when we get ready to drag out a new shape, but we don’t want to clear the screen several times a second as we drag out our shape.

Rather than forcing the entire view to be redrawn many times during our drag, we can use

setNeedsDisplayInRect: instead. setNeedsDisplayInRect: is an NSView method that marks a just one rectangular portion of a view’s region as needing redisplay. By using this, we can be more efficient by marking only the part of the view that is affected by the current drawing operation as needing to be redrawn.

We need to redraw not just the rectangle between firstTouch and lastTouch but any part of the screen encompassed by the current drag. If the user touches the screen and then scribbles all over and we redrew the only section between firstTouch and lastTouch, we’d leave a lot of stuff drawn on the screen that we don’t want.

The answer is to keep track of the entire area that’s been affected by a particular drag in a

CGRect instance variable. In touchesBegan:, we reset that instance variable to just the point where the user touched. Then in touchesMoved: and touchesEnded:, we use a Core Graph- ics function to get the union of the current rectangle and the stored rectangle, and we store

24594ch12.indd 420 6/25/09 6:06:14 PM

Download at Boykma.Com

CHAPTER 12: Drawing with Quartz and OpenGL 421

the resulting rectangle. We also use it to specify what part of the view needs to be redrawn.

This approach gives us a running total of the area impacted by the current drag.

Right now, we calculate the current rectangle in the drawRect: method for use in drawing the ellipse and rectangle shapes. We’ll move that calculation into a new method so that it can be used in all three places without repeating code. Ready? Let’s do it. Make the following changes to QuartzFunView.h:

#import <UIKit/UIKit.h>

#import "Constants.h"

@interface QuartzFunView : UIView { CGPoint firstTouch;

CGPoint lastTouch;

UIColor *currentColor;

ShapeType shapeType;

UIImage *drawImage;

BOOL useRandomColor;

CGRect redrawRect;

}

@property CGPoint firstTouch;

@property CGPoint lastTouch;

@property (nonatomic, retain) UIColor *currentColor;

@property ShapeType shapeType;

@property (nonatomic, retain) UIImage *drawImage;

@property BOOL useRandomColor;

@property (readonly) CGRect currentRect;

@property CGRect redrawRect;

@end

We declare a CGRect called redrawRect that we will use to keep track of the area that needs to be redrawn. We also declare a read-only property called currentRect, which will return that rectangle that we were previously calculating in drawRect:. Notice that it is a property with no underlying instance variable, which is okay, as long as we implement the accessor rather than relying on @synthesize to do it for us. We’ll still use the @synthesize keyword, but will write the accessor ourselves. @synthesize will create an accessor or mutator only if one doesn’t already exist in the class.

Switch over to QuartzFunView.m, and insert the following code at the top of the file:

#import "QuartzFunView.h"

@implementation QuartzFunView

@synthesize firstTouch;

@synthesize lastTouch;

@synthesize currentColor;

@synthesize shapeType;

24594ch12.indd 421 6/25/09 6:06:14 PM

Download at Boykma.Com

CHAPTER 12: Drawing with Quartz and OpenGL

422

@synthesize drawImage;

@synthesize useRandomColor;

@synthesize redrawRect;

@synthesize currentRect;

- (CGRect)currentRect { return CGRectMake (

(firstTouch.x > lastTouch.x) ? lastTouch.x : firstTouch.x, (firstTouch.y > lastTouch.y) ? lastTouch.y : firstTouch.y, fabsf(firstTouch.x - lastTouch.x),

fabsf(firstTouch.y - lastTouch.y));

} ...

Now, in the drawRect: method, delete the lines of code where we calculated currentRect, and change all references to currentRect to self.currentRect so that the code uses that new accessor we just created.

...

- (void)drawRect:(CGRect)rect { if (currentColor == nil)

self.currentColor = [UIColor redColor];

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetLineWidth(context, 2.0);

CGContextSetStrokeColorWithColor(context, currentColor.CGColor);

CGContextSetFillColorWithColor(context, currentColor.CGColor);

CGRect currentRect = CGRectMake (

(firstTouch.x > lastTouch.x) ? lastTouch.x : firstTouch.x, (firstTouch.y > lastTouch.y) ? lastTouch.y : firstTouch.y, fabsf(firstTouch.x - lastTouch.x),

fabsf(firstTouch.y - lastTouch.y));

switch (shapeType) { case kLineShape:

CGContextMoveToPoint(context, firstTouch.x, firstTouch.y);

CGContextAddLineToPoint(context, lastTouch.x, lastTouch.y);

CGContextStrokePath(context);

break;

case kRectShape:

CGContextAddRect(context, self.currentRect);

CGContextDrawPath(context, kCGPathFillStroke);

break;

case kEllipseShape:

CGContextAddEllipseInRect(context, self.currentRect);

24594ch12.indd 422 6/25/09 6:06:14 PM

Download at Boykma.Com

CHAPTER 12: Drawing with Quartz and OpenGL 423

CGContextDrawPath(context, kCGPathFillStroke);

break;

case kImageShape:

if (drawImage == nil)

self.drawImage = [UIImage imageNamed:@"iphone.png"];

CGFloat horizontalOffset = drawImage.size.width / 2;

CGFloat verticalOffset = drawImage.size.height / 2;

CGPoint drawPoint = CGPointMake(lastTouch.x - horizontalOffset, lastTouch.y - verticalOffset);

[drawImage drawAtPoint:drawPoint];

break;

default:

break;

} } ...

We also need to make some changes to in touchesEnded:withEvent: and

touchesMoved:withEvent:. We need to recalculate the space impacted by the current operation, and use that to indicate that only portion of our view needs to be redrawn:

...

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject];

lastTouch = [touch locationInView:self];

[self setNeedsDisplay];

if (shapeType == kImageShape) {

CGFloat horizontalOffset = drawImage.size.width / 2;

CGFloat verticalOffset = drawImage.size.height / 2;

redrawRect = CGRectUnion(redrawRect, CGRectMake(lastTouch.x - horizontalOffset, lastTouch.y - verticalOffset,

drawImage.size.width, drawImage.size.height));

} else

redrawRect = CGRectUnion(redrawRect, self.currentRect);

redrawRect = CGRectInset(redrawRect, -2.0, -2.0);

[self setNeedsDisplayInRect:redrawRect];

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject];

lastTouch = [touch locationInView:self];

[self setNeedsDisplay];

if (shapeType == kImageShape) {

CGFloat horizontalOffset = drawImage.size.width / 2;

24594ch12.indd 423 6/25/09 6:06:14 PM

Download at Boykma.Com

CHAPTER 12: Drawing with Quartz and OpenGL

424

CGFloat verticalOffset = drawImage.size.height / 2;

redrawRect = CGRectUnion(redrawRect,

CGRectMake(lastTouch.x - horizontalOffset,

lastTouch.y - verticalOffset, drawImage.size.width, drawImage.size.height));

}

redrawRect = CGRectUnion(redrawRect, self.currentRect);

[self setNeedsDisplayInRect:redrawRect];

} ...

With only a few additional lines of code, we reduced the amount of work necessary to redraw our view by getting rid of the need to erase and redraw any portion of the view that wasn’t been affected by the current drag. Being kind to the iPhone’s precious processor cycles like this can make a big difference in the performance of your applications, especially as they get more complex.

Một phần của tài liệu beginning iphone 3 development exploring the iphone sdk phần 8 ppt (Trang 41 - 45)

Tải bản đầy đủ (PDF)

(58 trang)