uiviewcalayeruibezierpathsubviewcashapelayer

changing size of CALayer also changes size of previously added layer's size


i'm trying to draw over UIImageView with this custome class and i would like to be able to change the size and color of lines being drawn by user. But every time i change the size or color of CALayer Previously added layer's color and size also changes then.

class DrawingImageView: UIImageView {
private lazy var path = UIBezierPath()
private lazy var previousTouchPoint = CGPoint.zero
var strokeColor: CGColor!
var strokeWidth: CGFloat!

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

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

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

    let shapeLayer = CAShapeLayer()
    shapeLayer.lineWidth = strokeWidth ?? 4
    shapeLayer.strokeColor = strokeColor ?? UIColor.black.cgColor
    shapeLayer.lineCap = .round
    shapeLayer.lineJoin = .round
    layer.addSublayer(shapeLayer)


    if let location = touches.first?.location(in: self) { previousTouchPoint = location }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesMoved(touches, with: event)
    
    if let location = touches.first?.location(in: self) {
        path.move(to: location)
        path.addLine(to: previousTouchPoint)
        previousTouchPoint = location

        let layerA = layer.sublayers?.last as! CAShapeLayer

        layerA.path = path.cgPath
    }
  }
}

this is how i assign that class to tempImageView

    let mainImageView = UIImageView()
    var tempImgView: DrawingImageView?

    mainImageView.frame = CGRect(x: 0, y: self.view.bounds.height / 7, width: imageWidth, height: imageHieght)
    mainImageView.contentMode = .scaleAspectFit
    mainImageView.layer.zPosition = 2
    mainImageView.isOpaque = false
    mainImageView.backgroundColor = .clear
    mainImageView.isUserInteractionEnabled = true
    mainImageView.tintColor = .clear
    self.view.addSubview(mainImageView)

    func DrawOverImage() {
    
    mainImageView.isHidden = true
    let drawnImageView = DrawingImageView(frame: mainImageView.frame)

    drawnImageView.image = mainImageView.image
    drawnImageView.contentMode = .scaleAspectFit
    drawnImageView.isUserInteractionEnabled = true
    drawnImageView.layer.zPosition = 3
    self.tempImgView?.isUserInteractionEnabled = true
    self.tempImgView = drawnImageView

    self.view.addSubview(tempImgView!)
}

i also tried creating array of CAShapeLayers and add layers to an array and then assign replace subLayer like View.layer.subLayers[0] = layers[0] but that also didn't work and i couldn't find any useful resource online.

any help or suggestion would be really appreciated, thanks.


Solution

  • That is because you are continuing the same path each time and so the latest stroke color and width gets applied to the whole path.

    Without looking at much of your other logic, I believe you should initialize a new path each time in your touchesBegan and this new path should be used in touchesMoved

    So in touchesBegan, try adding path = UIBezierPath() after layer.addSublayer(shapeLayer) and see if this gives you what you are looking for