views:

2103

answers:

3

I am making a simple iPhone drawing program as a personal side-project.

I capture touches event in a subclassed UIView and render the actual stuff to a seperate CGLayer. After each render, I call [self setNeedsLayout] and in the drawRect: method I draw the CGLayer to the screen context.

This all works great and performs decently for drawing rectangles. However, I just want a simple "freehand" mode like a lot of other iPhone applications have.

The way I thought to do this was to create a CGMutablePath, and simply:

CGMutablePathRef path;
-(void)touchBegan {
    path = CGMutablePathCreate();
}
-(void)touchMoved {
    CGPathMoveToPoint(path,NULL,x,y);
    CGPathAddLineToPoint(path,NULL,x,y);

}
-(void)drawRect:(CGContextRef)context {
      CGContextBeginPath(context);
      CGContextAddPath(context,path);
      CGContextStrokePath(context);
}

However, after drawing for more than 1 second, performance degrades miserably.

I would just draw each line into the off-screen CGLayer, if it were not for variable opacity! The less-than-100% opacity causes dots to be left on the screen connecting the lines. I have looked at CGContextSetBlendingMode() but alas I cannot find an answer.

Can anyone point me in the right direction? Other iPhone apps are able to do this with very good efficiency.

+1  A: 

The problem is that with CGStrokePath() the current mutable path gets closed and drawn and a new path is created when you move your finger. So you probably end up with a lot of paths for one touch "session", at least that's what your pseudocode seems to do.

You can try to begin a new mutable path when touches begin, use CGAddLineToPoint() when the touches move und end the path when touches end (much like your pseudocode shows). But in the draw method, you draw a copy of the current mutable path, and the actual mutable path is still being elongated until the touches end, so you only get one path for the whole touch session. After the touches end you can add the path permanently - you can for example put all paths into an array and iterate over them in the draw method.

Pascal
+1  A: 

What SanHolo said - plus you may want to throttle the adding of points, so it only adds a new point no more often than every 10ms, say (you'd need to play with the interval). You can do that with a simple timer.

Also, how are you instructing the view that it needs to redraw itself? You might want to throttle that too - and it could be on a longer interval than the point capturing (e.g. capture points no more than every 10ms, and redraw no more often than every 200ms - again you'd need to play with the numbers).

In both cases you'd need to make sure that, if nothing happens for longer than the interval the last point is captured, or the redraw is requested. That's where the timer comes in.

Phil Nash
A: 

How do you draw a copy of the current mutable path? I suspect that the intention is to draw the path without adding it to the current context to avoid the addition of too many paths. If so, how can the path be drawn without adding it to the current context?

qutaibah