Search code examples
iosswiftcalayercashapelayer

CAShapeLayer Image Not Appearing


I have an image.cgImage that I set as the contents to a CAShapeLayer() but it's not appearing. I force unwrap the image so I know that it definitely exists. The round shape appears but the image inside of it doesn't.

let shapeLayer = CAShapeLayer()
var didSubviewsLayout = false

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if !didSubviewsLayout {
        didSubviewsLayout = true

        setupShapeLayer()
    }
}

func setupShapeLayer() {
    
    shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 200, y: 200, width: 50, height: 50), cornerRadius: 25).cgPath
    
    shapeLayer.strokeColor = nil
    
    shapeLayer.fillColor = UIColor.orange.cgColor
    
    shapeLayer.contents = UIImage(named: "myIcon")!.cgImage // this doesn't crash so the image definitely exists
    
    view.layer.addSublayer(shapeLayer)
}

Solution

  • The problem is that your shape layer has no size. You have not given it a frame, so it is just an invisible dot at the top right corner. Unfortunately, you can still see the shape, so you're not aware of the fact that you're doing this all wrong. But the content doesn't draw, because the layer is just a dot, so it reveals your code's feet of clay.

    So, in this case, change

    shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 200, y: 200, width: 50, height: 50), cornerRadius: 25).cgPath
    

    To

    shapeLayer.frame = CGRect(x: 200, y: 200, width: 50, height: 50)
    shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 50, height: 50), cornerRadius: 25).cgPath
    

    Incidentally, that is not how to draw a circle correctly, so if that's your goal, stop it. Use ovalIn; that's what it's for. Complete example:

        let shapeLayer = CAShapeLayer()
    
        shapeLayer.frame = CGRect(x: 200, y: 200, width: 50, height: 50)
        shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 50, height: 50)).cgPath
        shapeLayer.strokeColor = UIColor.orange.cgColor
        shapeLayer.lineWidth = 4
        shapeLayer.fillColor = nil
        shapeLayer.contents = UIImage(named: "smiley")?.cgImage
    
        self.view.layer.addSublayer(shapeLayer)
    

    Result:

    enter image description here

    Note too that you are probably making this mistake with all your shape layers, not just this one, so you will want to go back through all the code in this app (or better, all the code you've ever written!) and write all your shape layers correctly. Layers need frames!