iosswiftsvgcore-animationpocketsvg

Draw rectangle around CAShape Layer


I am new in Swift and also in Core Animation. I want to create rectangle box around CAShapeLayer just like in this image, but I don't know.

In detail, I am parsing SVG image through PocketSVG and convert it's UIBezierPath into CAShapeLayer and after that I added these layers to UI Image View as sublayer.

here is the code where I am converting Path to CaShape Layer.

fileprivate func createLayer(path: SVGBezierPath) -> CAShapeLayer {
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = path.cgPath
    if let any = path.svgAttributes["stroke"] {
        shapeLayer.strokeColor = (any as! CGColor)
    }
    
    if let any = path.svgAttributes["fill"] {
        shapeLayer.fillColor = (any as! CGColor)
    }
    return shapeLayer
}

Below is the code, where I add layers to UIImage View (backgroundIV)

        for path in paths {
        path.apply(.init(scaleX: scale, y: scale))
        let layer = createLayer(path: path)
        layer.frame = self.backgroundIV.bounds
        self.backgroundIV.layer.addSublayer(layer)
    }

I also add TapGesture, RotateGesture, ScaleGesture and PanGesture to CA Shape Layers, Below is the code.

    @objc func rotateLayer(_ sender: UIRotationGestureRecognizer) {
    selectedLayer?.setAffineTransform(CGAffineTransform(rotationAngle: sender.rotation))
}

@objc func selectLayer(_ sender: UITapGestureRecognizer) {
    let point = sender.location(in: self.backgroundIV)
    guard let layers = self.backgroundIV.layer.sublayers
    else { return }
    var hitLayers: [CAShapeLayer] = []
    
    selectedLayer?.lineWidth = CGFloat(0)
    
    for subLayer in layers {
        if let thisLayer = subLayer as? CAShapeLayer,
           let pth = thisLayer.path {
            let layerPoint: CGPoint = thisLayer.convert(point, from: self.backgroundIV.layer)
            if pth.contains(layerPoint) {
                hitLayers.append(thisLayer)
            }
        }
    }
    
    selectedLayer = hitLayers.last
    selectedLayer?.strokeColor = UIColor.red.cgColor
    selectedLayer?.lineWidth = CGFloat(3)
    selectedLayerRect = CGRect(origin: point, size: CGSize(width: 100, height: 100))
    showPopupMenu()
    
}

@objc func moveLayer(_ recognizer: UIPanGestureRecognizer) {
    let p = recognizer.location(in: self.backgroundIV)
    selectedLayer?.position = p
}

@objc func scaleLayer(_ sender: UIPinchGestureRecognizer) {
    selectedLayer?.transform = CATransform3DMakeScale(sender.scale, sender.scale, 1)
}

In the last lines of SelectLayer method, I am highlight the selected layer by increasing it's stroke width and color.

selectedLayer?.strokeColor = UIColor.red.cgColor
selectedLayer?.lineWidth = CGFloat(3)

When I dis select layer, I just set it's lineWidth value to 0, which is not good practice.

Is there any way to create rectangle box like in this image?


Solution

  • This worked for me

        var selectedLayer: CAShapeLayer! {
        didSet {
            guard let layer = selectedLayer else {return}
            box.frame = layer.path?.boundingBox ?? CGRect()
            box.borderWidth = layer.contentsScale * 3
            box.borderColor = UIColor.red.cgColor
            self.updateControlView()
            layer.addSublayer(box)
        }
        willSet {
            self.controlView.isHidden = true
            box.removeFromSuperlayer()
        }
    }
    

    Output