Removing lagging latency in drawing UIBezierPath smooth lines in Swift

后端 未结 1 1989
死守一世寂寞
死守一世寂寞 2020-12-09 23:36

The code below draws smooth curved lines by overriding touches, but there is noticeable lagging or latency. The code uses addCurveToPoint and calls setNee

相关标签:
1条回答
  • 2020-12-10 00:04
    1. 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 to points[2] and adding a cubic curve to points[3].

      As you said, make sure to add this to a separate path, though. So, in 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()
          }
      }
      

      You could even marry this with iOS 9 predictive touches (as I described in my other answer), which could reduce lag even further.

    2. To take this resulting image and use it elsewhere, you can just grab the incrementalImage (which I renamed to snapshotImage, above), and drop it into an image view of the other view.

    For Swift 2 rendition, see previous revision of this answer.

    0 讨论(0)
提交回复
热议问题