I draw 3 squares in a - LayoutView
- (void)drawRect:(CGRect)rect
self.room1 = [UIBezierPath bezierPathWithRect:CGRectMake(81, 10, 60, 60)];
[self.normalColor setFill];
[self.room1 fill];
[[UIColor blackColor]setStroke];
self.room1.lineWidth = 1;
[self.room1 stroke];
then I find the correct UIBezierPath with
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touch here");
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
if ([self.room1 containsPoint:touchPoint])
{
// do stuff
NSLog(#"room1 %#" , self.room1);
[[UIColor redColor] setFill];
[self.room1 fill];
[self setNeedsDisplay];
}
}
this is working I touch room 1 and the log print "room1"
But how do I change the colour of the room1 ?
At the moment I get an error
: CGContextSetFillColorWithColor: invalid context 0x0. This is a serious error. ...
thanks for your help.
One way to accomplish this, is to keep track of the selected state in the touchesBegan method, and keep all the fill and setFill statements inside drawRect. In the following example, I toggle the selected state with each touch inside the square which alternates the color between blue and red.
#interface RDView ()
#property (strong,nonatomic) UIBezierPath *room1;
#property (strong,nonatomic) UIColor *normalColor;
#property (strong,nonatomic) UIColor *selectedColor;
#property (nonatomic) BOOL isSelected;
#end
#implementation RDView
-(id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
self.normalColor = [UIColor blueColor];
self.selectedColor = [UIColor redColor];
self.isSelected = NO;
}
return self;
}
- (void)drawRect:(CGRect)rect {
self.room1 = [UIBezierPath bezierPathWithRect:CGRectMake(81, 10, 60, 60)];
UIColor *colorToUse = (self.isSelected)? self.selectedColor : self.normalColor;
[colorToUse setFill];
[self.room1 fill];
[[UIColor blackColor]setStroke];
self.room1.lineWidth = 1;
[self.room1 stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [touches.anyObject locationInView:self];
if ([self.room1 containsPoint:touchPoint]){
self.isSelected = ! self.isSelected;
[self setNeedsDisplay];
}
}
Related
I'm developing a drawing app which lets user to draw one UIBezierpath just once over an image which has been drawn in the UIView by drawInRect:blendMode in drawRect method and it's uses over 80% of CPU and 55MB of RAM and when I traced it down in the time profiler, it was drawInRect:blendMode method that was causing most of the CPU usage. I have already done few optimizations but what further optimizations can I do for both drawing the UIImage which is being drawn using [image drawInRect:Rect]; and also for the UIBezierpath which user draws on top of it?
Thanks in advance.
#interface InsideView(){
CGRect imageRect;
CGRect targetBounds;
}
#property (strong, nonatomic) NSMutableArray *strokes;
#property (nonatomic, strong) UIImage * img;
#property (nonatomic, strong) UIBezierPath *drawingPath;
#end
#implementation InsideView
-(void)drawRect:(CGRect)rect{
targetBounds = self.layer.bounds;
imageRect = AVMakeRectWithAspectRatioInsideRect(self.img.size,targetBounds);
[self.img drawInRect:imageRect];
for (int i=0; i < [self.strokes count]; i++) {
UIBezierPath* tmp = (UIBezierPath*)[self.strokes objectAtIndex:i];
[[UIColor whiteColor] setStroke];
[tmp stroke];
}
}
- (CGRect)segmentBoundsFrom:(CGPoint)point1 to:(CGPoint)point2
{
CGRect dirtyPoint1 = CGRectMake(point1.x-10, point1.y-10, 50, 50);
CGRect dirtyPoint2 = CGRectMake(point2.x-10, point2.y-10, 50, 50);
return CGRectUnion(dirtyPoint1, dirtyPoint2);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
self.drawingPath = [UIBezierPath bezierPath];
self.drawingPath.lineWidth = 10;
self.drawingPath.lineCapStyle = kCGLineCapRound;
self.drawingPath.lineJoinStyle = kCGLineJoinRound;
[self.drawingPath moveToPoint:[[touches anyObject] locationInView:self]];
[self.strokes addObject:self.drawingPath];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
//Dirty Rect Calculations
CGPoint prevPoint = CGPathGetCurrentPoint(self.drawingPath.CGPath);
CGPoint point = [[touches anyObject] locationInView:self];
CGRect dirty = [self segmentBoundsFrom:prevPoint to:point];
[[self.strokes lastObject] addLineToPoint:point];
[self setNeedsDisplayInRect:dirty];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[[self.strokes lastObject] addLineToPoint:[[touches anyObject] locationInView:self]];
[self setNeedsDisplay];
}
I have the following codes to draw lines in a UIView. But once I start drawing, the view becomes dark grey. How can I make the background transparent? I already set the backgroundColor to clearColor, but the background is still dark gray. What did I miss?
#interface CanvasView () {
UIColor *colorLine;
UIBezierPath *pathBezier;
}
#end
#implementation CanvasView
#synthesize imgCached;
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self) {
self.backgroundColor = [UIColor clearColor];
[self setMultipleTouchEnabled:NO];
pathBezier = [[UIBezierPath alloc] init];
pathBezier.lineWidth = 5;
colorLine = [UIColor redColor];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[self.imgCached drawInRect:rect];
[pathBezier stroke];
}
- (void)drawCache {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);
[colorLine setStroke];
if(!self.imgCached) {
UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds];
[rectpath fill];
}
[self.imgCached drawAtPoint:CGPointZero];
[pathBezier stroke];
self.imgCached = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
- (void)clearGraphics {
self.imgCached = nil;
[pathBezier removeAllPoints];
[self setNeedsDisplay];
}
#pragma mark - Touch methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[touches allObjects] objectAtIndex:0];
[pathBezier moveToPoint:[touch locationInView:self]];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[touches allObjects] objectAtIndex:0];
[pathBezier addLineToPoint:[touch locationInView:self]];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[pathBezier addLineToPoint:p];
[self drawCache];
[self setNeedsDisplay];
[pathBezier removeAllPoints];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
p.s. the imgCached is:
#property (nonatomic, strong) UIImage *imgCached;
Replace your drawRect: and drawCache methods with the following:
- (void)drawRect:(CGRect)rect {
[self.imgCached drawInRect:rect];
[colorLine setStroke];
[pathBezier stroke];
}
- (void)drawCache {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
[colorLine setStroke];
[self.imgCached drawAtPoint:CGPointZero];
[pathBezier stroke];
self.imgCached = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
As was suggested to me in a prior StackOverflow question, I'm trying to improve my drawing method for letting my user draw lines/dots into a UIView. I'm now trying to draw using a CAShapeLayer instead of dispatch_async. This all works correctly, however, drawing into the CAShapeLayer continuously while touches are moving becomes slow and the path lags behind, whereas my old (inefficient I was told) code worked beautifully smooth and fast. You can see my old code commented out below.
Is there any way to improve the performance for what I want to do? Maybe I'm overthinking something.
I'd appreciate any help offered.
Code:
#property (nonatomic, assign) NSInteger center;
#property (nonatomic, strong) CAShapeLayer *drawLayer;
#property (nonatomic, strong) UIBezierPath *drawPath;
#property (nonatomic, strong) UIView *drawView;
#property (nonatomic, strong) UIImageView *drawingImageView;
CGPoint points[4];
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
self.center = 0;
points[0] = [touch locationInView:self.drawView];
if (!self.drawLayer)
{
CAShapeLayer *layer = [CAShapeLayer layer];
layer.lineWidth = 3.0;
layer.lineCap = kCALineCapRound;
layer.strokeColor = self.inkColor.CGColor;
layer.fillColor = [[UIColor clearColor] CGColor];
[self.drawView.layer addSublayer:layer];
self.drawView.layer.masksToBounds = YES;
self.drawLayer = layer;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
self.center++;
points[self.center] = [touch locationInView:self.drawView];
if (self.center == 3)
{
UIBezierPath *path = [UIBezierPath bezierPath];
points[2] = CGPointMake((points[1].x + points[3].x)/2.0, (points[1].y + points[3].y)/2.0);
[path moveToPoint:points[0]];
[path addQuadCurveToPoint:points[2] controlPoint:points[1]];
points[0] = points[2];
points[1] = points[3];
self.center = 1;
[self drawWithPath:path];
}
}
- (void)drawWithPath:(UIBezierPath *)path
{
if (!self.drawPath)
{
self.drawPath = [UIBezierPath bezierPath];
}
[self.drawPath appendPath:path];
self.drawLayer.path = self.drawPath.CGPath;
[self.drawLayer setNeedsDisplay];
// Below code worked faster and didn't lag behind at all really
/*
dispatch_async(dispatch_get_main_queue(),
^{
UIGraphicsBeginImageContextWithOptions(self.drawingImageView.bounds.size, NO, 0.0);
[self.drawingImageView.image drawAtPoint:CGPointZero];
[self.inkColor setStroke];
[path stroke];
self.drawingImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
*/
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.center == 0)
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:points[0]];
[path addLineToPoint:points[0]];
[self drawWithPath:path];
}
self.drawLayer = nil;
self.drawPath = nil;
}
This problem intrigued me as I've always found UIBezierPath/shapeLayer to be reletivly fast.
It's important to note that in your code above, you continues to add points to drawPath. As this increases, the appendPath method becomes a real resource burden. Similarly, there is no point in successively rendering the same points over and over.
As a side note, there is a visible performance difference when increasing lineWidth and adding lineCap (regardless of approach). For the sake of comparing Apples with Apples, in the test below, I've left both to default values.
I took your above code and changed it a little. The technique I've used is to add touchPoints to the BezierPath up to a per-determined number, before committing the current rendering to image. This is similar to your original approach, however, given that it's not happening with every touchEvent. it's far less CPU intensive. I tested both approaches on the slowest device I have (iPhone 4S) and noted that CPU utilization on your initial implementation was consistently around 75-80% whilst drawing. Whilst with the modified/CAShapeLayer approach, CPU utilization was consistently around 10-15% Memory usage also remained minimal with the second approach.
Below is the Code I used;
#interface MDtestView () // a simple UIView Subclass
#property (nonatomic, assign) NSInteger cPos;
#property (nonatomic, strong) CAShapeLayer *drawLayer;
#property (nonatomic, strong) UIBezierPath *drawPath;
#property (nonatomic, strong) NSMutableArray *bezierPoints;
#property (nonatomic, assign) NSInteger pointCount;
#property (nonatomic, strong) UIImageView *drawingImageView;
#end
#implementation MDtestView
CGPoint points[4];
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self) {
//
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
self.cPos = 0;
points[0] = [touch locationInView:self];
if (!self.drawLayer)
{
// this should be elsewhere but kept it here to follow your code
self.drawLayer = [CAShapeLayer layer];
self.drawLayer.backgroundColor = [UIColor clearColor].CGColor;
self.drawLayer.anchorPoint = CGPointZero;
self.drawLayer.frame = self.frame;
//self.drawLayer.lineWidth = 3.0;
// self.drawLayer.lineCap = kCALineCapRound;
self.drawLayer.strokeColor = [UIColor redColor].CGColor;
self.drawLayer.fillColor = [[UIColor clearColor] CGColor];
[self.layer insertSublayer:self.drawLayer above:self.layer ];
self.drawingImageView = [UIImageView new];
self.drawingImageView.frame = self.frame;
[self addSubview:self.drawingImageView];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if (!self.drawPath)
{
self.drawPath = [UIBezierPath bezierPath];
// self.drawPath.lineWidth = 3.0;
// self.drawPath.lineCapStyle = kCGLineCapRound;
}
// grab the current time for testing Path creation and appending
CFAbsoluteTime cTime = CFAbsoluteTimeGetCurrent();
self.cPos++;
//points[self.cPos] = [touch locationInView:self.drawView];
points[self.cPos] = [touch locationInView:self];
if (self.cPos == 3)
{
/* uncomment this block to test old method
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:points[0]];
points[2] = CGPointMake((points[1].x + points[3].x)/2.0, (points[1].y + points[3].y)/2.0);
[path addQuadCurveToPoint:points[2] controlPoint:points[1]];
points[0] = points[2];
points[1] = points[3];
self.cPos = 1;
dispatch_async(dispatch_get_main_queue(),
^{
UIGraphicsBeginImageContextWithOptions(self.drawingImageView.bounds.size, NO, 0.0);
[self.drawingImageView.image drawAtPoint:CGPointZero];
// path.lineWidth = 3.0;
// path.lineCapStyle = kCGLineCapRound;
[[UIColor redColor] setStroke];
[path stroke];
self.drawingImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSLog(#"it took %.2fms to draw via dispatchAsync", 1000.0*(CFAbsoluteTimeGetCurrent() - cTime));
});
*/
// I've kept the original structure in place, whilst comparing apples for apples. we really don't need to create
// a new bezier path and append it. We can simply add the points to the global drawPath, and zero it at an
// appropriate point. This would also eliviate the need for appendPath
// /*
[self.drawPath moveToPoint:points[0]];
points[2] = CGPointMake((points[1].x + points[3].x)/2.0, (points[1].y + points[3].y)/2.0);
[self.drawPath addQuadCurveToPoint:points[2] controlPoint:points[1]];
points[0] = points[2];
points[1] = points[3];
self.cPos = 1;
self.drawLayer.path = self.drawPath.CGPath;
NSLog(#"it took %.2fms to render %i bezier points", 1000.0*(CFAbsoluteTimeGetCurrent() - cTime), self.pointCount);
// 1 point for MoveToPoint, and 2 points for addQuadCurve
self.pointCount += 3;
if (self.pointCount > 100) {
self.pointCount = 0;
[self commitCurrentRendering];
}
// */
}
}
- (void)commitCurrentRendering{
CFAbsoluteTime cTime = CFAbsoluteTimeGetCurrent();
#synchronized(self){
CGRect paintLayerBounds = self.drawLayer.frame;
UIGraphicsBeginImageContextWithOptions(paintLayerBounds.size, NO, [[UIScreen mainScreen]scale]);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(context, kCGBlendModeCopy);
[self.layer renderInContext:context];
CGContextSetBlendMode(context, kCGBlendModeNormal);
[self.drawLayer renderInContext:context];
UIImage *previousPaint = UIGraphicsGetImageFromCurrentImageContext();
self.layer.contents = (__bridge id)(previousPaint.CGImage);
UIGraphicsEndImageContext();
[self.drawPath removeAllPoints];
}
NSLog(#"it took %.2fms to save the context", 1000.0*(CFAbsoluteTimeGetCurrent() - cTime));
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.cPos == 0)
{
/* //not needed
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:points[0]];
[path addLineToPoint:points[0]];
[self drawWithPath:path];
*/
}
if (self.cPos == 2) {
[self commitCurrentRendering];
}
// self.drawLayer = nil;
[self.drawPath removeAllPoints];
}
#end
How can I paint/draw a straight line with 2 fingers with the line being visible while the dragging or touching is still being done by the user? I've already tried simple painting using coregraphics but this one seems a bit complicated for me.
To Justin's point, just handle touchesBegan and touchesMoved.
Thus, if you subclass UIView, the CoreGraphics implementation might look like:
#interface CustomView ()
#property (nonatomic, strong) NSMutableArray *paths;
#property (nonatomic, strong) UIBezierPath *currentPath;
#end
#implementation CustomView
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self setMultipleTouchEnabled:YES];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.currentPath)
{
if (!self.paths)
self.paths = [NSMutableArray array];
self.currentPath = [UIBezierPath bezierPath];
self.currentPath.lineWidth = 3.0;
[self.paths addObject:self.currentPath];
[self touchesMoved:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count] != 2)
return;
[self.currentPath removeAllPoints];
__block NSInteger i = 0;
[touches enumerateObjectsUsingBlock:^(UITouch *touch, BOOL *stop) {
CGPoint location = [touch locationInView:self];
if (i++ == 0)
[self.currentPath moveToPoint:location];
else
[self.currentPath addLineToPoint:location];
}];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
self.currentPath = nil;
}
- (void)drawRect:(CGRect)rect
{
[[UIColor redColor] setStroke];
[[UIColor clearColor] setFill];
for (UIBezierPath *path in self.paths)
{
[path stroke];
}
}
- (void)reset
{
self.paths = nil;
[self setNeedsDisplay];
}
#end
Alternatively, you can also use Quartz 2D and define your own CAShapeLayer objects, and then either your view subclass or your view controller could do something like (needless to say, this is the view controller implementation; the view implementation should be obvious):
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#property (nonatomic, weak) CAShapeLayer *currentLayer;
#property (nonatomic, strong) UIBezierPath *currentPath;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setMultipleTouchEnabled:YES];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.currentLayer)
{
CAShapeLayer *layer = [CAShapeLayer layer];
layer.lineWidth = 3.0;
layer.strokeColor = [[UIColor redColor] CGColor];
layer.fillColor = [[UIColor clearColor] CGColor];
[self.view.layer addSublayer:layer];
self.currentLayer = layer;
self.currentPath = [UIBezierPath bezierPath];
[self touchesMoved:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count] != 2)
return;
[self.currentPath removeAllPoints];
__block NSInteger i = 0;
[touches enumerateObjectsUsingBlock:^(UITouch *touch, BOOL *stop) {
CGPoint location = [touch locationInView:self.view];
if (i++ == 0)
[self.currentPath moveToPoint:location];
else
[self.currentPath addLineToPoint:location];
}];
self.currentLayer.path = [self.currentPath CGPath];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
self.currentPath = nil;
self.currentLayer = nil;
}
- (IBAction)didTouchUpInsideClearButton:(id)sender
{
for (NSInteger i = [self.view.layer.sublayers count] - 1; i >= 0; i--)
{
if ([self.view.layer.sublayers[i] isKindOfClass:[CAShapeLayer class]])
[self.view.layer.sublayers[i] removeFromSuperlayer];
}
}
#end
For this latter approach, you need to add the QuartzCore.framework to your project.
I need to draw a line with dashes at regular gaps by sliding finger on the iPhone screen. I have written the following code for that and it is drawing the dashed line but the gaps are not regular and thus it is not looking good. I have also provided a link to the video showing the issue Please help!
Link to video:
https://dl.dropbox.com/u/56721867/Screen%20Recording%207.mov
CGPoint mid1 = midPoint__5(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint__5(currentPoint, previousPoint1);
CGContextRef context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
self.lineColor=[arrObj objectAtIndex:colorTag];
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGContextSetLineCap(context, kCGLineCapRound);//use for making circle ,rect or line effect===============
CGFloat dashArray[] = {2,8,2,8};
CGContextSetLineDash(context,2,dashArray, 4);
CGContextSetLineWidth(context, self.lineWidth);
self.lineColor=[arrObj objectAtIndex:[AppHelper userDefaultsForKey:#"ColorTag"].intValue];//color tag on button click
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextSetAlpha(context, 1.0);
CGBlendMode blendStyle=isErase?kCGBlendModeClear:kCGBlendModeNormal;
CGContextSetBlendMode(context,blendStyle);
CGContextStrokePath(context);
[super drawRect:rect];
It's rather unclear from the code, but based on the video and a hunch I'd guess a single swipe with the finger ends up as several line segments. This means that the dashing starts from scratch several times, creating the effect in the video. You probably need a NSMutableArray where you put all those currentPoint into, and then draw them all as one line every time you redraw.
This question has more information on how to draw lines in this way: iPhone Core Graphics thicker dashed line for subview
Here is the code to do what you are trying, it uses Quartz instead of CoreGraphics. You can use either Quartz or CoreGraphics.
//
// TestView.h
//
//
// Created by Syed Arsalan Pervez on 2/18/13.
// Copyright (c) 2013 SAPLogix. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface TestView : UIView
{
NSMutableArray *_points;
}
#end
//
// TestView.m
//
//
// Created by Syed Arsalan Pervez on 2/18/13.
// Copyright (c) 2013 SAPLogix. All rights reserved.
//
#import "TestView.h"
#interface UserPoint : NSObject
{
BOOL _start;
BOOL _end;
CGPoint _point;
}
#property (assign, nonatomic) BOOL start;
#property (assign, nonatomic) BOOL end;
#property (assign, nonatomic) CGPoint point;
#end
#implementation UserPoint
#end
#implementation TestView
- (id)init
{
self = [super init];
if (self)
{
_points = [NSMutableArray new];
}
return self;
}
- (UserPoint *)addPoint:(CGPoint)point
{
UserPoint *_userPoint = [UserPoint new];
_userPoint.point = point;
[_points addObject:_userPoint];
[_userPoint release];
return [_points lastObject];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
UserPoint *_userPoint = [self addPoint:[touch locationInView:self]];
_userPoint.start = YES;
_userPoint = nil;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
UserPoint *_userPoint = [self addPoint:[touch locationInView:self]];
_userPoint = nil;
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
UserPoint *_userPoint = [self addPoint:[touch locationInView:self]];
_userPoint.end = YES;
_userPoint = nil;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
[[UIColor blackColor] setStroke];
[[UIColor blackColor] setFill];
UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:15];
CGFloat dash[] = {15,20};
[path setLineDash:dash count:2 phase:0];
[path setLineCapStyle:kCGLineCapRound];
CGPoint lastPoint;
for (UserPoint *_point in _points)
{
if (_point.start || _point.end)
[path moveToPoint:_point.point];
else
{
[path addQuadCurveToPoint:_point.point controlPoint:lastPoint];
}
lastPoint = _point.point;
}
[path stroke];
}
- (void)dealloc
{
[_points release];
[super dealloc];
}
#end