Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Beginning iOS5 Development.pdf
Скачиваний:
7
Добавлен:
09.05.2015
Размер:
15.6 Mб
Скачать

CHAPTER 16: Drawing with Quartz and OpenGL

589

case kEllipseShape: CGContextAddEllipseInRect(context, currentRect); CGContextDrawPath(context, kCGPathFillStroke); break;

case kImageShape: {

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;

}

}

NOTE: Notice that in the switch statement, we added curly braces around the code under case kImageShape:. The compiler has a problem with variables declared in the first line after a case statement. These curly braces are our way of telling the compiler to stop complaining. We could also have declared horizontalOffset before the switch statement, but this approach keeps the related code together.

First, we calculate the center of the image, since we want the image drawn centered on the point where the user last touched. Without this adjustment, the image would be drawn with the upper-left corner at the user’s finger, also a valid option. We then make a new CGPoint by subtracting these offsets from the x and y values in lastTouch.

CGFloat horizontalOffset = drawImage.size.width / 2; CGFloat verticalOffset = drawImage.size.height / 2;

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

Now, we tell the image to draw itself. This line of code will do the trick:

[drawImage drawAtPoint:drawPoint];

Optimizing the QuartzFun Application

Our application does what we want, but we should consider a bit of optimization. In our little 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 BIDQuartzFunView.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 be erased and redrawn, even if only a tiny bit changed. We do want to erase the screen when we get ready to

www.it-ebooks.info

590

CHAPTER 16: Drawing with Quartz and OpenGL

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 a UIView method that marks just one rectangular portion of a view’s region as needing redisplay. By using this method, 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 touched the screen and then scribbled all over, but we redrew only the section between firstTouch and lastTouch, we would leave a lot of stuff drawn on the screen that we don’t want to remain.

The solution is to keep track of the entire area that has 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 Graphics function to get the union of the current rectangle and the stored rectangle, and we store the resulting rectangle. We also use it to specify which part of the view needs to be redrawn. This approach gives us a running total of the area impacted by the current drag.

Now, we’ll 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 BIDQuartzFunView.h:

#import <UIKit/UIKit.h> #import "BIDConstants.h"

@interface BIDQuartzFunView : UIView @property (nonatomic) CGPoint firstTouch; @property (nonatomic) CGPoint lastTouch;

@property (nonatomic, strong) UIColor *currentColor; @property (nonatomic) ShapeType shapeType;

@property (nonatomic, strong) UIImage *drawImage; @property (nonatomic) 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:.

Switch over to BIDQuartzFunView.m, and insert the following code at the top of the file, after the existing @synthesize statement:

@synthesize redrawRect, currentRect;

- (CGRect)currentRect {

return CGRectMake (firstTouch.x,

www.it-ebooks.info

CHAPTER 16: Drawing with Quartz and OpenGL

591

firstTouch.y,

lastTouch.x - firstTouch.x, lastTouch.y - firstTouch.y);

}

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

- (void)drawRect:(CGRect)rect {

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetLineWidth(context, 2.0);

CGContextSetStrokeColorWithColor(context, currentColor.CGColor);

CGContextSetFillColorWithColor(context, currentColor.CGColor); CGRect currentRect = CGRectMake(firstTouch.x,

firstTouch.y,

lastTouch.x - firstTouch.x, lastTouch.y - firstTouch.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); CGContextDrawPath(context, kCGPathFillStroke);

break;

case kImageShape:{

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 touchesEnded:withEvent: and touchesMoved:withEvent:. We will recalculate the space impacted by the current operation and use that to indicate that only a portion of our view needs to be redrawn. Replace the existing touchesEnded: and touchesMoved: methods with these new versions:

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

lastTouch = [touch locationInView:self];

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]