2016-01-25 50 views
1

下面的代码通过覆盖触摸绘制平滑的曲线,但存在明显的滞后或延迟。该代码使用addCurveToPoint并在每触摸4个触点后调用setNeedsDisplay,这会导致跳动的外观,因为图形跟不上手指的移动。为了消除滞后或潜在的等待时间,可以使用addQuadCurveToPointaddLineToPoint临时填充触点1,2,3(触及触点4)。删除Swift中绘制UIBezierPath平滑线的滞后延迟

  1. 如何这实际上代码来实现删除使用显示最终曲线之前的临时线路和QuadCurved线感知滞后?

  2. 如果低于类被附接到一个UIView(例如viewOne或self),如何touchesEnded后使该图的一个拷贝到另一个UIView类(例如viewTwo)以外?

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

回答

2
  1. 是,增加了曲线每隔几个点会给它一个口吃的滞后。所以,是的,您可以通过在points[1]中添加一条线来减少这种影响,向points[2]添加四条曲线并向points[3]添加三次曲线。

    正如你所说,确保将其添加到单独的路径,但。所以,在斯威夫特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() 
        } 
    } 
    

    你甚至可以与iOS 9个预测触摸嫁给这个(我描述in my other answer),这可能会进一步减少滞后。要获取此结果图像并在其他地方使用它,您可以抓取incrementalImage(我将其重命名为snapshotImage,如上所示),然后将其放到其他视图的图像视图中。

对于Swift 2再现,请参阅前面的revision of this answer

+0

非常感谢。辉煌!感谢您的明确解释。我会试着抓住'snapshotImage'。我注意到了代码中的两个文物。 1 - 在绘图,抬起手指,然后再次开始绘制时,看起来前一绘图的最后部分被移除并用细线代替。这种情况有时会发生。图片:http://i.imgur.com/LIpfF4q.png 2 - 绘图时出现一个平直的角落。这也仅在有时发生。 Image:http://i.imgur.com/QyWpXG0.png 什么是造成这些文物? – user4806509

+1

好的。是的,问题是只有在调用'drawRect'时才配置属性,但'constructIncrementalImage'不会。但是,'UIBezierPath'的配置实际上不属于'drawRect'。当路径被实例化时,它应该被设置。所以,我写了一个方法来实例化和配置路径,并在需要创建路径时使用它。请参阅上面的修订答案 – Rob

+0

优秀!非常感谢,它运作良好。 – user4806509