Reputation:
I'm creating a drawing app. In the beginning my drawing is smooth. When I draw a bunch of circles for a long period of time, my drawing starts to get edgy. Perhaps it's the fact that an array can't handle too many points?
DV.swift:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
lastPoint = touches.first!.locationInView(self)
self.setNeedsDisplay()
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
var newPoint = touches.first!.locationInView(self)
lines.append(Line(start: lastPoint, end: newPoint))
lastPoint = newPoint
self.setNeedsDisplay()
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
var veryFirstPoint = touches.first!.locationInView(self)
lines.append(Line(start: lastPoint, end:veryFirstPoint))
self.setNeedsDisplay()
}
override func drawRect(rect: CGRect) {
var context = UIGraphicsGetCurrentContext()
CGContextBeginPath(context)
for line in lines {
CGContextMoveToPoint(context,line.start.x , line.start.y)
CGContextAddLineToPoint(context, line.end.x, line.end.y)
}
CGContextSetRGBFillColor(context, 0, 0, 0, 1)
CGContextSetLineWidth(context, 5)
CGContextStrokePath(context)
}
Example tested on my iPad mini 4: On the left side is the jagged numbers after drawing a bunch of loops. the right side is the first few numbers I drew and they're smooth.
Upvotes: 1
Views: 119
Reputation: 15331
You are right. You are having the CPU redraw many paths every time you add a new "line", that is after every touch. This becomes increasingly processor intensive the more lines you have. One solution is to only redraw the "dirty" parts of your view. You can do this by using setNeedsDisplayInRect(rect:CGRect)
instead of setNeedsDisplay()
. For example you can add the extension:
extension CGRect{
static func rectWithTwoPoints(p1:CGPoint,p2:CGPoint) -> CGRect
{
return CGRectMake(min(p1.x, p2.x),min(p1.y, p2.y),fabs(p1.x - p2.x),fabs(p1.y - p2.y));
}
}
This will give me a rect enclosing any two points. Now in your touchesMoved:
and touchesEnded:
we can call setNeedsDisplayInRect:
like so:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let newPoint = touches.first!.locationInView(self)
lines.append(Line(start: lastPoint, end: newPoint))
lastPoint = newPoint
self.setNeedsDisplayInRect(CGRectInset(CGRect.rectWithTwoPoints((lines.last?.start)!, p2: (lines.last?.end)!),-10.0,-10.0)) //would be better to not force unwrap and use magic numbers, but you get the idea
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let veryFirstPoint = touches.first!.locationInView(self)
lines.append(Line(start: lastPoint, end:veryFirstPoint))
self.setNeedsDisplayInRect(CGRectInset(CGRect.rectWithTwoPoints((lines.last?.start)!, p2: (lines.last?.end)!),-10.0,-10.0))
}
Using CGRectInset
to expand our rect a bit because we have a path width that if we only redraw the rect returned by rectWithTwoPoints would take into account.
Upvotes: 1