Suppression de la latence retardée dans le dessin des lignes lisses UIBezierPath dans Swift

Le code ci-dessous dessine des lignes courbes lisses en écrasant les touches, mais il y a un retard ou une latence notable. Le code utilise addCurveToPoint et appelle setNeedsDisplay après chaque 4 points de contact, ce qui provoque une apparence nerveuse car le dessin ne suit pas les mouvements des doigts. Pour supprimer le retard ou la latence perçue, les points de contact 1, 2, 3 (menant au sharepoint contact 4) pourraient être temporairement remplis avec addQuadCurveToPoint et addLineToPoint .

  1. Comment cela peut-il être réalisé dans le code pour supprimer le retard perçu en utilisant une ligne Ligne et QuadCurved temporaire avant d'afficher une ligne courbe finale?

  2. Si la class ci-dessous est attachée à un UIView (par exemple viewOne ou self ), comment faire une copy du dessin à un autre UIView dehors de la class (par exemple viewTwo) après touchesEnded ?

      // 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. } } 

  1. Oui, append une courbe tous les quelques points lui donnera un décalage de bégaiement. Donc, oui, vous pouvez réduire cet effet en ajoutant une ligne aux points[1] , en ajoutant une courbe quadruple aux points[2] et en ajoutant une courbe cubique aux points[3] .

    Comme vous l'avez dit, assurez-vous d'append ceci à un path séparé, cependant. Donc, dans Swift 3/4:

     class SmoothCurvedLinesView: UIView { var strokeColor = UIColor.blue var lineWidth: CGFloat = 20 var snapshotImage: UIImage? private var path: UIBezierPath? private var temporaryPath: UIBezierPath? private var points = [CGPoint]() override func draw(_ rect: CGRect) { snapshotImage?.draw(in: rect) strokeColor.setStroke() path?.stroke() temporaryPath?.stroke() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { points = [touch.location(in: self)] } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let point = touch.location(in: self) points.append(point) updatePaths() setNeedsDisplay() } private func updatePaths() { // update main path while points.count > 4 { points[3] = CGPoint(x: (points[2].x + points[4].x)/2.0, y: (points[2].y + points[4].y)/2.0) if path == nil { path = createPathStarting(at: points[0]) } path?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2]) points.removeFirst(3) temporaryPath = nil } // build temporary path up to last touch point if points.count == 2 { temporaryPath = createPathStarting(at: points[0]) temporaryPath?.addLine(to: points[1]) } else if points.count == 3 { temporaryPath = createPathStarting(at: points[0]) temporaryPath?.addQuadCurve(to: points[2], controlPoint: points[1]) } else if points.count == 4 { temporaryPath = createPathStarting(at: points[0]) temporaryPath?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2]) } } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { finishPath() } override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) { finishPath() } private func finishPath() { constructIncrementalImage() path = nil setNeedsDisplay() } private func createPathStarting(at point: CGPoint) -> UIBezierPath { let localPath = UIBezierPath() localPath.move(to: point) localPath.lineWidth = lineWidth localPath.lineCapStyle = .round localPath.lineJoinStyle = .round return localPath } private func constructIncrementalImage() { UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0) strokeColor.setStroke() snapshotImage?.draw(at: .zero) path?.stroke() temporaryPath?.stroke() snapshotImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() } } 

    Vous pouvez même vous marier avec iOS 9 touches prédictives (comme je l'ai décrit dans mon autre réponse ), ce qui pourrait réduire le décalage encore plus loin.

  2. Pour prendre cette image résultante et l'utiliser ailleurs, vous pouvez simplement saisir l' incrementalImage (que j'ai renommé snapshotImage , ci-dessus), et le déposer dans une vue d'image de l'autre vue.

Pour le rendu de Swift 2, voir la révision précédente de cette réponse .