How to keep a round imageView round using auto layout?
Solution 1
Unfortunately you cannot do this using cornerRadius
and autolayout — the CGLayer
is not affected by autolayout, so any change in the size of the view will not change the radius which has been set once causing, as you have noticed, the circle to lose its shape.
You can create a custom subclass of UIImageView
and override layoutSubviews
in order to set the cornerRadius
each time the bounds of the imageview change.
EDIT
An example might look something like this:
class Foo: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
let radius: CGFloat = self.bounds.size.width / 2.0
self.layer.cornerRadius = radius
}
}
And obviously you would have to constrain the Foobar
instance's width to be the same as the height (to maintain a circle). You would probably also want to set the Foobar
instance's contentMode
to UIViewContentMode.ScaleAspectFill
so that it knows how to draw the image (this means that the image is likely to be cropped).
Solution 2
Setting radius in viewWillLayoutSubviews will solve the problem
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
profileImageView.layer.cornerRadius = profileImageView.frame.height / 2.0
}
Solution 3
create new interface in your .h file like
@interface CornerClip : UIImageView
@end
and implementation in .m file like
@implementation cornerClip
-(void)layoutSubviews
{
[super layoutSubviews];
CGFloat radius = self.bounds.size.width / 2.0;
self.layer.cornerRadius = radius;
}
@end
now just give class as "CornerClip" to your imageview. 100% working... Enjoy
Solution 4
First of all, I should mention that u can get a circle shape for your UIView/UIImageView only if the width and height will be equal. It's important to understand. In all other cases (when width != height), you won't get a circle shape because the initial shape of your UI instance was a rectangle.
OK, with this so UIKit SDK provides for developers a mechanism to manipulate the UIview's layer instance to change somehow any of layer's parameters, including setting up a mask
to replace the initial shape of UIView element with the custom one. Those instruments are IBDesignable/IBInspectable. The goal is to preview our custom views directly through Interface Builder.
So using those keywords we can write our custom class, which will deal only with the single condition whether we need to round corners for our UI element or not.
For example, let's create the class extended from the UIImageView.
@IBDesignable
class UIRoundedImageView: UIImageView {
@IBInspectable var isRoundedCorners: Bool = false {
didSet { setNeedsLayout() }
}
override func layoutSubviews() {
super.layoutSubviews()
if isRoundedCorners {
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(ovalIn:
CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width, height: bounds.height
)).cgPath
layer.mask = shapeLayer
}
else {
layer.mask = nil
}
}
}
After setting the class name for your UIImageView element (where the dog picture is), in your storyboard, you will get a new option, appeared in the Attributes Inspector menu (details at the screenshot).
The final result should be like this one.
Solution 5
It seems when you add one view as a subview of another that netted view will not necessarily have the same height as its superview. That's what the problem seems like. The solution is to not add your imageView as a subview, but have it on top of your backgroundView. In the image below I'm using a UILabel as my backgroundView.
Also in your case, when you're setting the cornerRadius use this: let radius: CGFloat = self.bounds.size.height / 2.0
.
Related videos on Youtube
CHM
Updated on March 29, 2020Comments
-
CHM over 3 years
How do I turn a rectangular image view into a circular image view that can hold shape in auto layout without setting width and height restraints? Thereby allowing the imageView to define it’s size, and size bigger and smaller relative to objects around it with leading, trailing, top, and bottom constraints.
I asked a similar question the other day, but I think this might be posed in a more concise way. Thanks so much!
EDIT
Ok, I started over to make this as simple as possible. I have a view named "Cell" and a UIImageView named "dog" within the cell, and that's it. I don't have "unable to simultaneously satisfy constraints" in the console anymore, just two simple views using auto layout. I'm still trying to use this code to round the UIImageView:
profileImageView.layer.cornerRadius = profileImageView.frame.size.width / 2 profileImageView.clipsToBounds = true
Here is the cell constraint setup:
Here is the profile pic constraint setup:
Here is the result without the code, no rounding, but nice and square:
Here is the result with the code to round:
This makes no sense to me, because without the rounding code the image is square, and with the code it's diamond shaped. If it's square shouldn't it be a circle with no issues?
EDIT 2
Here's what happens when I remove the bottom constraint and add a multiplier of .637 for equal height to superview.
-
Gavin Hope about 8 yearsHow are you currently making the image view round?
-
CHM about 8 years@GavinHope I'm calling it into a cell in a tableviewcontroller and using: cell.ProfileImageView.layer.cornerRadius = cell.ProfileImageView.frame.size.width / 2 cell.ProfileImageView.clipsToBounds = true
-
lee about 7 yearsIf your imageview had the Width != Height then you can find solution here: stackoverflow.com/questions/29685055/…
-
-
CHM about 8 yearsHi @petahchristian I'm locking a 1:1 ratio and also using top, leading, trailing, and bottom constraints, but when I size down or up I lose the perfect circle. When i go bigger i get an oblong oval, and smaller, more of a diamond shape
-
Randel S about 8 yearsAre you getting "Unable to simultaneously satisfy constraints" in the console? If you're having top, leading, trailing, and bottom constraints, than those constraints could be pulling or push your imageView cause the aspect ratio constraint to brake.
-
CHM about 8 yearsHi @Rupert thanks for info! I'm just getting my feet wet with Swift. That sounds quite a bit over my head. How would that look...or do you maybe have a link to something with a similar solve that i could visually check out?
-
CHM about 8 yearsthanks so much! When you say "constrain the Foobar instance's width to be the same as the height" would setting 1:1 aspect ratio work for that or would i have to give it an absolute width and height?
-
Admin about 8 years@CHM Yes. As Randel pointed out, you've over constrained the imageView. You can't have it size in 4 directions and also remain circular.
-
CHM about 8 years@RandelG.Smith yes, i get that in the console. I'll run some constraint tests tomorrow, and add piece by piece to see what breaks it. Thanks for your help guys. I'll update when i figure it out.
-
Rupert about 8 yearsIn your sub-class, for example, you could add a constraint
let constraint: NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0.0); self.addConstraint(constraint)
-
CHM about 8 years@RandelG.Smith I added simplified diagrams to the original question. Take a look, it gives a more visual depiction of what's happening.
-
CHM about 8 years@PetahChristian I added simplified diagrams to the original question. Take a look, it gives a more visual depiction of what's happening.
-
Randel S about 8 years@CHM Try removing the bottom constraint on the "dog" image.
-
CHM about 8 years@RandelG.Smith That I tried removing it and adding .637 as a multiplier, i'll post what the result was above in an edit
-
CHM about 8 years@RandelG.Smith Where did your answer go?
-
Randel S about 8 yearsI'm about to add a different answer. The one I deleted wasn't correct.
-
CHM about 8 years@RandelG.Smith I got that multiplier to work. I think the number was different because i had margins. So i ran it in the simulator and the picture is still diamond shaped on 5s, perfect circle on 6, and almost a circle on 6+
-
CHM about 8 yearsahhh...I couldn't get it to work. I tried it out of the container with this code based on what you said: let radius: CGFloat = profileImageView.bounds.size.height / 2.0 profileImageView.clipsToBounds = true
-
Randel S about 8 yearsHere's the xcode project showing what I did. github.com/RGSSoftware/ImageViewConstraints
-
CHM about 8 yearsRandel, that totally works doing it that way. My only issue is i'm running these images in custom tableview cells that have their own class files with elements set as IBOutlets, so i cannot figure out how to use the func viewDidLayoutSubviews, because i'm calling them dynamically into the tableView controller with cellForRowAtIndexPath and using dequeueReusableCellWithIdentifier
-
Noah G. almost 7 yearsI've been looking for an answer for OVER A YEAR! Thank you so, so , so much!
-
Photon Point over 6 yearsThanks. And select Clip To Bounds for image on the storyboard.
-
dannysood about 6 yearsThis should be the accepted answer. Simple, practical conforms to ideals of Apple and works perfectly.
-
Azharhussain Shaikh about 6 yearsOmar Albeik Thank you :)
-
Mehul Thakkar almost 6 yearsthis worked for me for iOS 11, but when i am testing the same thing in iOS 10, it clearly show jerk.. smooth transmission not happening in iOS 10
-
Rupert almost 6 years@MehulThakkar It's hard to know without more details but I suspect that this doesn't have anything to do with this solution. Using
cornerRadius
obviously involves compositing the view which incurs a performance hit. Usually this is not noticeable but perhaps you are using complicated views in a table view or something similar. -
Amit Kumar almost 6 yearsThank you so much.. You saved my lot of time.
-
J. Doe over 5 yearsthis is so bad XD
-
JWhitey over 4 yearsShould probably be
viewDidLayoutSubviews
? -
Sohaib Siddique over 4 yearsviewWillLayoutSubviews() or viewDidLayoutSubviews() both trigger on every UI changing in viewController, for example, while typing on keyboard both method trigger on every click. In this situation should we use these methods? is that safe? I want to cornerRadius of a button which has an aspect ratio in autolayout