The code below draws smooth curved lines by overriding touches, but there is noticeable lagging or latency. The code uses addCurveToPoint
and calls setNeedsDisplay
after every 4 touch points which causes a jumpy appearance as the drawing doesn't keep up with finger movements. To remove the lagging or perceived latency, touch points 1, 2, 3 (leading up to touch point 4) could be temporarily filled with addQuadCurveToPoint
and addLineToPoint
.
How can this actually be achieved in code to remove perceived lagging by using a temporary Line and QuadCurved line before displaying a final Curved line?
If the below class is attached to one
UIView
(e.g. viewOne orself
), how do I make a copy of the drawing to anotherUIView
outside the class (e.g. viewTwo) aftertouchesEnded
?// ViewController.swift import UIKit class drawSmoothCurvedLinesWithLagging: UIView { let path=UIBezierPath() var incrementalImage:UIImage? var points = [CGPoint?](count: 5, repeatedValue: nil) var counter:Int? var strokeColor:UIColor? required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override func drawRect(rect: CGRect) { autoreleasepool { incrementalImage?.drawInRect(rect) strokeColor = UIColor.blueColor() strokeColor?.setStroke() path.lineWidth = 20 path.lineCapStyle = CGLineCap.Round path.stroke() } } override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { counter = 0 let touch: AnyObject? = touches.first points[0] = touch!.locationInView(self) } override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { let touch: AnyObject? = touches.first let point = touch!.locationInView(self) counter = counter! + 1 points[counter!] = point if counter == 2{ //use path.addLineToPoint ? //use self.setNeedsDisplay() ? } if counter == 3{ //use path.addQuadCurveToPoint ? //use self.setNeedsDisplay() ? } if counter == 4{ points[3]! = CGPointMake((points[2]!.x + points[4]!.x)/2.0, (points[2]!.y + points[4]!.y)/2.0) path.moveToPoint(points[0]!) path.addCurveToPoint(points[3]!, controlPoint1: points[1]!, controlPoint2: points[2]!) self.setNeedsDisplay() points[0]! = points[3]! points[1]! = points[4]! counter = 1 } } override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { self.drawBitmap() self.setNeedsDisplay() path.removeAllPoints() counter = 0 } override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) { self.touchesEnded(touches!, withEvent: event) } func drawBitmap(){ UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, 0.0) strokeColor?.setStroke() if((incrementalImage) == nil){ let rectPath:UIBezierPath = UIBezierPath(rect: self.bounds) UIColor.whiteColor().setFill() rectPath.fill() } incrementalImage?.drawAtPoint(CGPointZero) path.stroke() incrementalImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() } } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
Yes, adding a curve every few points will give it a stuttering lag. So, yes, you can reduce this affect by adding a line to
points[1]
, adding a quad curve topoints[2]
and adding a cubic curve topoints[3]
.As you said, make sure to add this to a separate path, though. So, in Swift 3/4:
You could even marry this with iOS 9 predictive touches (as I described in my other answer), which could reduce lag even further.
To take this resulting image and use it elsewhere, you can just grab the
incrementalImage
(which I renamed tosnapshotImage
, above), and drop it into an image view of the other view.For Swift 2 rendition, see previous revision of this answer.