bao le
bao le

Reputation: 1395

UIBezierPath line disappears when drawing another one

I'm trying to draw multiple with UIbezierPath but whenever I try to draw the 2nd one, the first one disappears. Tried to create multiple BezierPath and put them into an array but I got this error when it run into case:ended

test[2687:238999] [Unknown process name] CGPathCloseSubpath: no current point.

This is my code


 private lazy var lineShape: CAShapeLayer = {
        let lineShape = CAShapeLayer()
        lineShape.strokeColor = UIColor.blue.cgColor
        lineShape.lineWidth = 2.0

        return lineShape
    }()
    
 var bezierPathArr:NSMutableArray = []
 private var panGestureStartPoint: CGPoint = .zero
 private lazy var panRecognizer: UIPanGestureRecognizer = {
        return UIPanGestureRecognizer(target: self, action: #selector(panGestureCalled(_:)))
    }()

 @objc func panGestureCalled(_: UIPanGestureRecognizer) {
        let linePath = UIBezierPath()
        let currentPanPoint = panRecognizer.location(in: self.view)
        switch panRecognizer.state {
        case .began:
            panGestureStartPoint = currentPanPoint
            self.view.layer.addSublayer(lineShape)
            bezierPathArr.add(linePath)

        case .changed:
            linePath.move(to: panGestureStartPoint)
            linePath.addLine(to: currentPanPoint)
            lineShape.path = linePath.cgPath

        case .ended:
            let finalPath:UIBezierPath = bezierPathArr.lastObject as! UIBezierPath
            finalPath.close()
    
        default: break
        }
    }


Upvotes: 1

Views: 175

Answers (1)

richardpiazza
richardpiazza

Reputation: 1589

Because of the lazy creation of your CAShapeLayer, whenever you are adding a layer to your view, you are using the same object reference. That along with setting the path on that reference would give you the behavior you are experiencing.

In order to achieve the behavior expected, you need to create and store a new CAShapeLayer for each of your completed gestures. Here's a working example:

class ViewController: UIViewController {
    
    private lazy var panRecognizer: UIPanGestureRecognizer = {
        return UIPanGestureRecognizer(target: self, action: #selector(panGestureCalled(_:)))
    }()
    
    /// The path that is being drawn (resets on .began, closes on .ended)
    private var currentPath: UIBezierPath = .init()
    /// All of the layers added to the view
    private var shapes: [CAShapeLayer] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addGestureRecognizer(panRecognizer)
    }

    @objc private func panGestureCalled(_ sender: UIPanGestureRecognizer) {
        let point = sender.location(in: self.view)
        switch sender.state {
        case .began:
            // Reset the path
            currentPath = .init()
            // Set the starting point
            currentPath.move(to: point)
            // Creates a new layer when gesture starts.
            let layer = newShapeLayer()
            // Save the layer with all shapes.
            shapes.append(layer)
            // Add the layer to the view
            view.layer.addSublayer(layer)
        case .changed:
            // Update the current path to the new point
            currentPath.addLine(to: point)
        case .ended:
            // Close/Finish the path
            currentPath.close()
        default:
            break
        }
        
        // Update the most-current shape with the path being drawn.
        shapes.last?.path = currentPath.cgPath
    }
    
    /// Creates a new `CAShapeLayer`
    private func newShapeLayer() -> CAShapeLayer {
        let layer = CAShapeLayer()
        layer.frame = view.bounds
        layer.strokeColor = UIColor.blue.cgColor
        layer.lineWidth = 2.0
        return layer
    }
}

Upvotes: 1

Related Questions