Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS: frame.size.width/2 doesn't produce a circle on every device

I'm aware that the formulae frame.size.width/2 should produce a circle border, however in XCode I am currently experiencing some discrepancies.

I have two test devices (iPhone6 and 5th gen iPod touch) I also have the simulator running. Both my devices display correctly but the simulator draws my circle as a rounded rectangle:

enter image description here

The code I am using to achieve this (although very simple) is:

imgAvatar.layer.masksToBounds = true
imgAvatar.clipsToBounds = true
imgAvatar.layer.cornerRadius = imgAvatar.frame.size.width/2
imgAvatar.layer.borderWidth = 5
imgAvatar.layer.borderColor = UIColor.whiteColor().CGColor

Is there any reason why this is happening? It's driving me insane!

UPDATE To clear confusion, the UIImageView is declared in my storyboard as 190x190 it also has a 1:1 aspect ratio constraint applied to it to ensure it maintains a proportional width and height.

UPDATE 2 To put any suspicions regarding my auto-layout constraints to bed, I have attached the below image which shows the constraints set for imgAvatar. As you can see a the width and height match and the AR is set to ensure it maintains that 1:1 ratio. I hope that clears up any further doubts

enter image description here

ANSWER Leo pointed out an extremely practical and reusable solution to fix this problem, using Swift extensions one can ensure that a given UIImage is always square, thus always generating a circle, I have posted Leo's solution below:

extension UIImage {
    var circleMask: UIImage? {
        let square = CGSize(width: min(size.width, size.height), height: min(size.width, size.height))
        let imageView = UIImageView(frame: .init(origin: .init(x: 0, y: 0), size: square))
        imageView.contentMode = .scaleAspectFill
        imageView.image = self
        imageView.layer.cornerRadius = square.width/2
        imageView.layer.borderColor = UIColor.white.cgColor
        imageView.layer.borderWidth = 5
        imageView.layer.masksToBounds = true
        UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, scale)
        defer { UIGraphicsEndImageContext() }
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        imageView.layer.render(in: context)
        return UIGraphicsGetImageFromCurrentImageContext()
    }
}

imgAvatar.image = yourImage.circleMask
like image 500
Halfpint Avatar asked Apr 16 '15 20:04

Halfpint


2 Answers

You can create an extension to apply the mask and border straight to your image so it will always work disregarding the screen size / scale:

edit/update:

Xcode 11 • Swift 5 or later

extension UIImage {
    var circleMask: UIImage? {
        let square = CGSize(width: min(size.width, size.height), height: min(size.width, size.height))
        let imageView = UIImageView(frame: .init(origin: .init(x: 0, y: 0), size: square))
        imageView.contentMode = .scaleAspectFill
        imageView.image = self
        imageView.layer.cornerRadius = square.width/2
        imageView.layer.borderColor = UIColor.white.cgColor
        imageView.layer.borderWidth = 5
        imageView.layer.masksToBounds = true
        UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, scale)
        defer { UIGraphicsEndImageContext() }
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        imageView.layer.render(in: context)
        return UIGraphicsGetImageFromCurrentImageContext()
    }
}

imgAvatar.image = yourImage.circleMask
like image 132
Leo Dabus Avatar answered Oct 16 '22 07:10

Leo Dabus


Well, Autolayout should be the issue. As it can be seen the height of the imgAvatar on right is greater than the height of the imgAvatar on left.

The size of imgAvatar on right is 190 x 190 and the one on left is 200 x 200, but on the left the corner radius which you are setting is 190/2 i.e. 95px whereas it should be 100px.

Kindly set Height & Width Constraint in your nib file for imgAvatar, if you do not want the imgAvatar to resize by 1:1.

  1. Select imgAvatar in your nib.
  2. Editor > Pin > Height
  3. Select imgAvatar in your nib.
  4. Editor > Pin > Width

OR

Try moving your code to viewDidLayoutSubviews

OR

Subclass your UIImageView and set its corner radius in its awakeFromNib method

Kindly let us know if it worked. Thanks!

like image 8
Burhanuddin Sunelwala Avatar answered Oct 16 '22 06:10

Burhanuddin Sunelwala