How to keep a round imageView round using auto layout?

28,733

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.

screenshot

Also in your case, when you're setting the cornerRadius use this: let radius: CGFloat = self.bounds.size.height / 2.0.

Share:
28,733

Related videos on Youtube

CHM
Author by

CHM

Updated on March 29, 2020

Comments

  • CHM
    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:

    screenshot 1

    Here is the profile pic constraint setup:

    screenshot 2

    Here is the result without the code, no rounding, but nice and square:

    screenshot 3

    Here is the result with the code to round:

    screenshot 4

    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.

    screenshot 5

    • Gavin Hope
      Gavin Hope about 8 years
      How are you currently making the image view round?
    • CHM
      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
      lee about 7 years
      If your imageview had the Width != Height then you can find solution here: stackoverflow.com/questions/29685055/…
  • CHM
    CHM about 8 years
    Hi @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
    Randel S about 8 years
    Are 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
    CHM about 8 years
    Hi @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
    CHM about 8 years
    thanks 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
    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
    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
    Rupert about 8 years
    In 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
    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
    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
    Randel S about 8 years
    @CHM Try removing the bottom constraint on the "dog" image.
  • CHM
    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
    CHM about 8 years
    @RandelG.Smith Where did your answer go?
  • Randel S
    Randel S about 8 years
    I'm about to add a different answer. The one I deleted wasn't correct.
  • CHM
    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
    CHM about 8 years
    ahhh...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
    Randel S about 8 years
    Here's the xcode project showing what I did. github.com/RGSSoftware/ImageViewConstraints
  • CHM
    CHM about 8 years
    Randel, 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.
    Noah G. almost 7 years
    I've been looking for an answer for OVER A YEAR! Thank you so, so , so much!
  • Photon Point
    Photon Point over 6 years
    Thanks. And select Clip To Bounds for image on the storyboard.
  • dannysood
    dannysood about 6 years
    This should be the accepted answer. Simple, practical conforms to ideals of Apple and works perfectly.
  • Azharhussain Shaikh
    Azharhussain Shaikh about 6 years
    Omar Albeik Thank you :)
  • Mehul Thakkar
    Mehul Thakkar almost 6 years
    this 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
    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
    Amit Kumar almost 6 years
    Thank you so much.. You saved my lot of time.
  • J. Doe
    J. Doe over 5 years
    this is so bad XD
  • JWhitey
    JWhitey over 4 years
    Should probably be viewDidLayoutSubviews?
  • Sohaib Siddique
    Sohaib Siddique over 4 years
    viewWillLayoutSubviews() 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