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:
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
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
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
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With