简体   繁体   中英

How can i draw smooth freehand drawing using shapelayer and bezierpath over an image

 let path = UIBezierPath()
 shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.lineWidth = 3
        path.move(to: startPoint)
        path.addLine(to: point)
        shapeLayer.lineJoin = kCALineJoinRound
        shapeLayer.path = path.cgPath
        tempImage.layer.addSublayer(shapeLayer)

I am using the above code. Calling the above code each tome the touch is moved. I am not getting a continuous drawing . only the final portion of my drawing is visible.Is there anything i am missing here.

I achieved a smooth drawing with UIBezierPath and CAShapeLayer . Here the approach is having a temporary bezier path. If you want to draw on top of an image probably you can try something like this drawingLayer.content = yourImage.cgImage .

Swift 4

class DrawingView: UIView {

      private var drawingLayer: CAShapeLayer?
      private var currentPath: UIBezierPath?
      private var temporaryPath: UIBezierPath?
      private var points = [CGPoint]()

      var drawColor = UIColor.blue

      var lineWidth: CGFloat = 2.0

      var opacity: CGFloat = 0.8

      var sublayers: [CALayer] {
        return self.layer.sublayers ?? [CALayer]()
      }

      // MARK: Init

      override public init(frame: CGRect) {
        super.init(frame: frame)

        commonInit()
      }

      required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        commonInit()
      }

      private func commonInit() {
        self.layer.removeAllAnimations()
        self.layer.masksToBounds = true  // Restrict the drawing within the canvas
        self.backgroundColor = UIColor.white
        self.isMultipleTouchEnabled = false
      }

      // MARK: Drawing

      override func draw(_ rect: CGRect) {
        // TODO: This orveriding is still required. Need to find a way to remove this
      }

      override func draw(_ layer: CALayer, in ctx: CGContext) {

        let drawingLayer = self.drawingLayer ?? CAShapeLayer()
        drawingLayer.contentsScale = UIScreen.main.scale

        drawingLayer.lineWidth = lineWidth
        drawingLayer.opacity = Float(opacity)
        drawingLayer.lineJoin = .round
        drawingLayer.lineCap = .round
        drawingLayer.fillColor = UIColor.clear.cgColor
        drawingLayer.miterLimit = 0
        drawingLayer.strokeColor = drawColor.cgColor

        let linePath = UIBezierPath()

        if let tempPath = temporaryPath, let bezierPath = currentPath {
          linePath.append(tempPath)
          linePath.append(bezierPath)
          drawingLayer.path = linePath.cgPath
        }

        if self.drawingLayer == nil {
          self.drawingLayer = drawingLayer
          self.layer.addSublayer(drawingLayer)
        }
      }

      // MARK: - Touches

      override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let point = touches.first?.preciseLocation(in: self) else {
          return
        }
        points.removeAll()
        points.append(point)
      }

      override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let point = touches.first?.preciseLocation(in: self) else {
          return
        }
        points.append(point)
        updatePaths()
        layer.setNeedsDisplay()
      }

      override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

        // single touch support
        if points.count == 1 {
          currentPath = createPathStarting(at: points[0])
          currentPath?.lineWidth = self.lineWidth / 2.0
          currentPath?.addArc(withCenter: points[0], radius: lineWidth / 4.0, startAngle: 0, endAngle: .pi * 2.0, clockwise: true)
        }

        finishPath()
      }

      override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
        finishPath()
      }

      // MARK: - Bezier paths Management

      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 currentPath == nil {
            currentPath = createPathStarting(at: points[0])
          }

          currentPath?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2])
          points.removeFirst(3)
          temporaryPath = nil
        }

        // build temporary path up to last touch point
        switch points.count {
        case 2:
          temporaryPath = createPathStarting(at: points[0])
          temporaryPath?.addLine(to: points[1])
          break
        case 3:
          temporaryPath = createPathStarting(at: points[0])
          temporaryPath?.addQuadCurve(to: points[2], controlPoint: points[1])
          break
        case 4:
          temporaryPath = createPathStarting(at: points[0])
          temporaryPath?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2])
          break
        default:
          break
        }
      }

      private func finishPath() {

        // add temp path to current path to reflect the changes in canvas
        if let tempPath = temporaryPath {
          currentPath?.append(tempPath)
        }

        currentPath = nil
      }

      private func createPathStarting(at point: CGPoint) -> UIBezierPath {
        let localPath = UIBezierPath()
        localPath.move(to: point)
        return localPath
      }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM