Search code examples
iosviewcalayeruibezierpath

issue adding layer around a circular view's border


I am adding a circular layer around my circular view and setting its position to be the center of the view but the layer gets added at a different position. The following is the code.

class CustomView: UIView {
    
    let outerLayer = CAShapeLayer()
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.layer.addSublayer(outerLayer)
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        self.backgroundColor = UIColor.systemBlue
        self.layer.cornerRadius = self.bounds.height/2
        self.layer.borderWidth = 2.0
        self.layer.borderColor = UIColor.white.cgColor

        let outerLayerFrame = self.bounds.insetBy(dx: -5.0, dy: -5.0)
        outerLayer.frame = outerLayerFrame
        
        let path = UIBezierPath(ovalIn: outerLayerFrame)
        outerLayer.path = path.cgPath
        outerLayer.position = self.center

        outerLayer.strokeColor = UIColor.systemBlue.cgColor
        outerLayer.fillColor = UIColor.clear.cgColor
        outerLayer.lineWidth = 3
        
    }
}

enter image description here

Can anyone please tell me what is wrong here.


Solution

  • You want to set your layer(s) properties when the view is instantiated / initialized, but its size can (and usually does) change between then and when auto-layout adjusts it to the current device dimensions.

    Unlike the UIView itself, layers do not auto-resize.

    So, you want to set framing and layer paths in layoutSubviews():

    class CustomView: UIView {
        
        let outerLayer = CAShapeLayer()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            configure()
        }
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            configure()
        }
    
        // layoutSubviews is where you want to set your size
        //  and corner radius values
        override func layoutSubviews() {
            super.layoutSubviews()
    
            // make self "round"
            self.layer.cornerRadius = self.bounds.height/2
    
            // add a round path for outer layer
            let path = UIBezierPath(ovalIn: bounds)
            outerLayer.path = path.cgPath
            
        }
        
        private func configure() {
            
            self.backgroundColor = UIColor.systemBlue
            
            // setup layer properties here
            self.layer.borderWidth = 2.0
            self.layer.borderColor = UIColor.white.cgColor
            
            outerLayer.strokeColor = UIColor.systemBlue.cgColor
            outerLayer.fillColor = UIColor.clear.cgColor
            outerLayer.lineWidth = 3
            
            // add the sublayer here
            self.layer.addSublayer(outerLayer)
    
        }
        
    }
    

    Result (with the view set to 120 x 120 pts):

    enter image description here