2016-01-25 88 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