0

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?

Ali Akbar
  • 7
  • 5

4 Answers4

0

Try this..
I hope this helps you...

import UIKit
class ViewController : UIViewController {

let containerView: UIView = {
    let view = UIView()
    view.backgroundColor = .clear
    return view
}()

override func viewDidLoad() {
    super.viewDidLoad()
    
    containerView.frame = CGRect(x: view.frame.width/2 - 100, y: view.frame.height/2 - 100, width: 200, height: 200)
    view.addSubview(containerView)
    
    drawRectangle()
}

private func drawRectangle() {
    
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0, y: 0))
    path.addLine(to: CGPoint(x: 200, y: 0))
    path.addLine(to: CGPoint(x: 200, y: 200))
    path.addLine(to: CGPoint(x: 0, y: 200))
    path.addLine(to: CGPoint(x: 0, y: 0))
    
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = path.cgPath
    shapeLayer.strokeColor = UIColor.red.cgColor
    shapeLayer.fillColor = UIColor.black.cgColor
    shapeLayer.lineWidth = 3
    
    containerView.layer.addSublayer(shapeLayer)
    }
}
bdeviOS
  • 449
  • 1
  • 6
0

If I understand correctly, your question is "How to draw a dashed outline"?

If so, you can set the .lineDashPattern on your shape layer.

For example:

    selectedLayer?.strokeColor = UIColor.red.cgColor
    selectedLayer?.lineWidth = 3.0
    
    // set the dash pattern for the stroke
    selectedLayer?.lineDashPattern = [10, 10]
DonMag
  • 69,424
  • 5
  • 50
  • 86
0

This is the answer of my question

    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

Ali Akbar
  • 7
  • 5
-1

If you want to draw a box around the layer's frame, that's easy. Just set aLayer.borderWidth=1 and aLayer.borderColor=UIColor.someColor.cgColor.

The system will add a border around the layer for you.

However, if your layers' frames are bigger than the paths they contain your rectangles might be bigger than you want.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • I am doing exactly this but I want to make rectangle box when I select layer – Ali Akbar Nov 03 '21 at 12:46
  • What does "select a layer" mean? iOS doesn't have that concept. That's something you will have to implement in your program. You'll need to add a `hitTest()` method on the parent layer that contains your shape layers, keep track of which layer(s) is/are selected, and toggle the selected layers' `borderWidth` between 1 and 0 as the user selects/deselects layers. – Duncan C Nov 03 '21 at 12:54
  • If you want help implementing a selection system for layers you will need to greatly expand your question to explain how your app is structured, what "Select a layer" means, etc, and describe what you're working on. – Duncan C Nov 03 '21 at 12:56
  • I edit the question as per your suggestion – Ali Akbar Nov 04 '21 at 05:57