Need a fresh pair of eyes, mine have stopped working and can't make sense of my own code anymore...
I am trying to make a drawing app with a pen on paper style of drawing. Here's how it is suppose to work:
- user touches, app grabs location
- a var is set to tell drawRect to configure CGcontext, create a new path, move to point, etc (because I always gett errors/warnings in my nslog whenever I do anything with CGcontext outside the drawRect method)
- var is then set to determine whether to place a dot or a line when the user lifts finger
- if the user drags, the var is changed to tell drawRect to draw a line and [self setneedsdisplay] is called
- every time the user places their finger on the screen a timer is activated, every 5 seconds (or until they lift their finger up) the app 'captures' the screen contents, sticks it in an image and wipes the screen, replacing the image and continues drawing (in effect cache'ing the image so all these lines don't have to be re-drawn)
UPDATE #1 So I re-wrote it (because it was obviously really bad and not working...) and ended up with this:
- (void)drawRect:(CGRect)rect {
[_incrImage drawInRect:rect]; /* draw image... this will be blank at first
and then supposed to be filled by the Graphics context so that when the view
is refreshed the user is adding lines ontop of an image (but to them it looks
like a continuous drawing)*/
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapSquare);
CGContextSetLineJoin(UIGraphicsGetCurrentContext(), kCGLineJoinRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), _brushW);
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), [UIColor colorWithRed:_brushR green:_brushG blue:_brushB alpha:_brushO].CGColor);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
CGContextAddPath(UIGraphicsGetCurrentContext(), _pathref);
CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathStroke);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
_drw = 2; //set this to draw a dot if the user taps
UITouch *touch = [touches anyObject];
_cp = [touch locationInView:self];
_lp = _cp;
CGPathRelease(_pathref); /* this line and the line below is to clear the
path so the app is drawing as little as possible (the idea is that as
the user draws and lifts their finger, the drawing goes into _incrImage
and then the contents is cleared and _incrImage is applied to the
screen so there is as little memory being used as possible and so the
user feels as if they're adding to what they've drawn when they touch
the device again) */
_pathref = CGPathCreateMutable();
CGPathMoveToPoint(_pathref, NULL, _lp.x, _lp.y);
touch = nil;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
_drw = 1; //user moved their finger, this was not a tap so they want
//to draw a line
UITouch *touch = [touches anyObject];
_cp = [touch locationInView:self];
CGPathAddLineToPoint(_pathref, NULL, _cp.x, _cp.y);
[self setNeedsDisplay]; //as the user moves their finger, it draws
_lp = _cp;
touch = nil;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
_cp = [touch locationInView:self];
switch (_drw) { //logic to determine what to draw when the user
//lifts their finger
case 1:
//line
CGPathAddLineToPoint(_pathref, NULL, _cp.x, _cp.y);
break;
case 2:
//dot
CGPathAddArc(_pathref, NULL, _cp.x, _cp.y, _brushW, 0.0, 360.0, 1);
break;
default:
break;
}
/* here's the fun bit, the Graphics context doesn't seem to be going
into _incrImage and therefore is not being displayed when the user
goes to continue drawing after they've drawn a line/dot on the screen */
UIGraphicsBeginImageContext(self.frame.size);
CGContextAddPath(UIGraphicsGetCurrentContext(), _pathref); /* tried adding
my drawn path to the context and then adding the context to the image
before the user taps down again and the path is cleared... not sure
why it isn't working. */
[_incrImage drawAtPoint:CGPointZero];
_incrImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setNeedsDisplay]; //finally, refresh the contents
touch = nil;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
My new problem is that everything is erased when drawRect is called and that '_incrImage' is not getting the contents of the current graphics and displaying them
__old, I guess no longer needed but keeping here for reference___
here is the relevant code:
- (void)drawRect:(CGRect)rect {
/* REFERENCE: _drw: 0 = clear everything
1 = cache the screen contents in the image and display that
so the device doesn't have to re-draw everything
2 = set up new path
3 = draw lines instead of a dot at current point
4 = draw a 'dot' instead of a line
*/
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapSquare);
CGContextSetLineJoin(UIGraphicsGetCurrentContext(), kCGLineJoinRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), _brushW);
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), [UIColor colorWithRed:_brushR green:_brushG blue:_brushB alpha:_brushO].CGColor);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
switch (_drw) {
case 0:
//clear everything...this is the first draw... it can also be called to clear the view
UIGraphicsBeginImageContext(self.frame.size);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [UIColor clearColor].CGColor);
CGContextFillRect(UIGraphicsGetCurrentContext(), self.frame);
CGContextFlush(UIGraphicsGetCurrentContext());
_incrImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[_incrImage drawAtPoint:CGPointZero];
[_incrImage drawInRect:rect];
break;
case 1:
//capture the screen content and stick it in _incrImage...
then apply_incrImage to screen so the user can continue drawing ontop of it
_incrImage = UIGraphicsGetImageFromCurrentImageContext();
[_incrImage drawAtPoint:CGPointZero];
[_incrImage drawInRect:rect];
break;
case 2:
//begin path and set everything up, this is called when touchesBegan: fires...
_incrImage = UIGraphicsGetImageFromCurrentImageContext();
[_incrImage drawAtPoint:CGPointZero];
[_incrImage drawInRect:rect];
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _p.x, _p.y);
break;
case 3:
//add lines, this is after the path is created and set...this is fired when touchesMoved: gets activated and _drw is set to draw lines instead of adding dots
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), _p.x, _p.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
break;
case 4:
//this is fired when touchesEnd: is activated... this sets up ready if the app needs to draw a 'dot' or the arc with a fill...
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _p.x, _p.y);
CGContextAddArc(UIGraphicsGetCurrentContext(), _p.x, _p.y, _brushW, 0.0, 360.0, 1);
CGContextFillPath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
break;
default:
break;
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
dTimer = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(timeUP) userInfo:Nil repeats:YES];
_drw = 2;
UITouch *touch = [touches anyObject];
_p = [touch locationInView:self];
[self setNeedsDisplay];
_drw = 4;
}
- (void)timeUP {
_drw = 1;
[self setNeedsDisplay];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
_drw = 3;
UITouch *touch = [touches anyObject];
_p = [touch locationInView:self];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event // (2)
{
[dTimer invalidate];
UITouch *touch = [touches anyObject];
_p = [touch locationInView:self];
[self setNeedsDisplay];
[self timeUP];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
My questions are:
- is this efficient? or is there a better way to do this? and
- Why is this not drawing? I get nothing but I can see my memory being used...