uiviewinterface-buildercornerradiusibinspectable

Circular UIView Not A Full Circle


I have an interesting issue, but I'm not sure how to solve it. I am attempting to extend UIView with an @IBInspectable. However, with this method the corner radius seems to be set from the nib files default side, not the actual size of the view.

So, when in IB I set the "View as" to iPhoneSE and build for iPhoneSE, the view is a circle. However, if I build for iPhone7, the corners are not fully rounded into a circle. Conversely, if I set "View as" to iPhone7 and build for iPhone7, the view is a circle, However, if I build for iPhoneSE the corners are over-rounded.

Pictures and code below:

Extension

extension UIView {

    @IBInspectable var cornerRadius:Double {
        get {
            return Double(layer.cornerRadius)
        }
        set {
            layer.cornerRadius = CGFloat(newValue)
            layer.masksToBounds = newValue > 0
        }
    }

    @IBInspectable var circleView:Bool {
        get {
            return layer.cornerRadius == min(self.frame.width, self.frame.height) / CGFloat(2.0) ? true : false
        }
        set {
            if newValue {
                layer.cornerRadius = min(self.frame.width, self.frame.height) / CGFloat(2.0)
                layer.masksToBounds = true
            }
            else{
                layer.cornerRadius = 0.0
                layer.masksToBounds = false
            }
        }
    }

}

"View as" set as iPhoneSE in IB

Built for iPhoneSE

enter image description here

Build for iPhone 7

enter image description here

"View as" set as iPhone7

Build for iPhone SE

enter image description here

Build for iPhone 7

enter image description here

IB Settings enter image description here enter image description here


Solution

  • You will only get a full circle if your view is a square.

    If you're view is a rectangle and you set the cornerRadius with a value less than half the smallest dimension, you will get the second view and if it's a value greater than that you will get the diamond shaped one.

    Check your constraints and you should compute the cornerRadius after the view finishes layout in viewDidLayout so you get the correct size.

    Here is a Playground showing this (I've added an animation to show better the issue):

    import UIKit
    import PlaygroundSupport
    
    extension UIView
    {
      func addCornerRadiusAnimation(from: CGFloat, to: CGFloat, duration: CFTimeInterval)
      {
        let animation = CABasicAnimation(keyPath:"cornerRadius")
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        animation.fromValue = from
        animation.toValue = to
        animation.duration = duration
        self.layer.add(animation, forKey: "cornerRadius")
        self.layer.cornerRadius = to
      }
    }
    
    let v = UIView(frame: CGRect(x: 0, y: 0, width: 350, height: 450))
    PlaygroundPage.current.liveView = v
    
    let squareView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
    squareView.backgroundColor = .red
    squareView.layer.masksToBounds = true
    v.addSubview(squareView)
    var cornerRadius = CGFloat(0.5*min(squareView.frame.width,
                                       squareView.frame.height))
    squareView.addCornerRadiusAnimation(from: 0, to: cornerRadius, duration: 5)
    
    let rectangleView = UIView(frame: CGRect(x: 10, y: 200, width: 100, height: 200))
    rectangleView.backgroundColor = .blue
    rectangleView.layer.masksToBounds = true
    v.addSubview(rectangleView)
    cornerRadius = CGFloat(0.5*min(rectangleView.frame.width,
                                       rectangleView.frame.height))
    rectangleView.addCornerRadiusAnimation(from: 0, to: cornerRadius, duration: 5)
    
    let diamondView = UIView(frame: CGRect(x: 200, y: 200, width: 100, height: 200))
    diamondView.backgroundColor = .green
    diamondView.layer.masksToBounds = true
    v.addSubview(diamondView)
    cornerRadius = CGFloat(0.5*max(diamondView.frame.width,
                                   diamondView.frame.height))
    diamondView.addCornerRadiusAnimation(from: 0, to: cornerRadius, duration: 5)
    

    Playground output