Rounded UIImageView - ios

I'm trying to get a rounded UIImageView but it seems to render differently on different devices;
Looks like this on an iPhone Xr:
Looks like this on an iPhone 7:
I have a height constraint of 60 and the following code:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.userAvatar.layer.cornerRadius = self.userAvatar.frame.height / 2
self.userAvatar.layer.masksToBounds = false
self.userAvatar.clipsToBounds = true
self.userAvatar.layer.borderWidth = 0
}
Any ideas?

It seems to me that you have given the image leading and trailing constraints instead of a fixed width.
To achieve a circle give image view width equal to height.
This happens due to different widths of devices.

If you're managing this view using Interface Builder (i.e. Storyboard or XIB), you can enforce a square shape (which becomes a circle when combined with the rounded corners you already have) for the view directly from there by defining a constraint for its Aspect Ratio. No need to code anything.
Control-drag (like you do to create Outlets, Actions, etc.) from the image view to itself, and the following popup will appear.
Select Aspect Ratio, which will create a constraint matching whatever the view's current ratio is (in this example, it's 15:8). If the view was already square, the constraint created should already be correct.
If not, you can find that constraint by clicking the following icon (for the Size inspector):
From there, you can double-click on that constraint to edit it, and change the Multiplier to 1:1:
In fact, an even easier option is, once you've Control-dragged from the view to itself, hold down Alt/Opt and the option displayed in the popup will change to Aspect Ratio (1:1), meaning you can set it directly from there without even having to edit the constraint.

Constrain the height equal to the width.
And, create a simple UIImageView subclass:
class RoundedImageView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.height / 2
}
}
The frame can (and will) change based on view lifecycle. By updating the cornerRadius in layoutSubviews() it will keep it "round".

Related

Change View Position based on ImageView size

I have Imageview and UIView on View Controller. If Imageview is nil or image is not available then UIView replace its postion.Do any know how is it possible using auto layout?
For trying purpose, I have fixed height and width of both(Imageview and UIView). Imageview have "top 8 pixel" and "Horizontally in container" margin. UIView have "top 0 from Imageview" and "Horizontally in container" margin. Set Imageview to nil but it doesn't work.
A good suggestion would be to add both of the image view and the view in a stackView and follow the steps mentioned in: UIStackView Distribution Fill Equally.
However, you can achieve what are you asking for by adding additional constraint between the bottom view and the top layout guide:
and then, set its priority value to be less than the default (1000) -in my example I set it to 500- and the its constant value to 0:
Its appearance should be displayed as dotted line, meaning that there is another constraint -with a higher priority value- deciding the x axis of the view, if this constraint has been removed/deactivated the dotted one should be activated.
Finally, make sure that if there is no available image you have to remove image view from its super view (call imageView.removeFromSuperview()), setting it as hidden or setting its alpha to 0.0 doesn't activate the dotted constraint:
class ViewController: UIViewController {
//...
#IBOutlet weak var imageView: UIImageView!
// I'm checking in 'viewDidLoad' method just for describing purposes,
// of course, you can do the check when needed...
override func viewDidLoad() {
// if there is something wrong, you should call:
imageView.removeFromSuperview()
}
//...
}
The output would be:
Take the outlet of height constraint of UIImageView and do below code
if (imageview == nil){
imageViewHeightConstraint.constant = 0
}
else{
imageViewHeightConstraint.constant = 60
}
And other query you can ask.
You can manage this by giving priority to the constraints using auto layout. Give your UIView top space from ImageView and the TopLayout. Assign a lower priority to the constraint (any value less than 1000) where you have given top space from top layout guide. If your image is nil , remove the image view from the view using imageview.RemoveFromSuperView(), the UIView will automatically take the next constraint i.e. from TopLayout guide. I am attaching a screenshot where to find the priority of the constraint on storyBoard.

Swift: Views not adjusting to programmatic constraints

I have a view that I am making hidden at the bottom of the screen, and want the scrollView above it to adjust and fill the void space.
The view at the bottom of the screen is a GADBannerView and has a fixed height of 50 (bannerHeight). The scroll view above it has a constraint to the bottom of the container that equals 50 (scrollConstraint). See photo.
In viewDidLoad is am setting these constraints to the following:
bannerHeight.constant = 0
scrollConstraint.constant = 0
This is causing the bannerView did disappear but the scroll view is staying in it's original position and not filling the void space.
You can force the superview to take into account the change of the constraint because this does not happen automatically. Add your code to viewDidLayoutSubviews() instead or simply call view.layoutIfNeeded() after you set the constants to 0 in the viewDidLoad().
If this does not work, you can try this alternative approach:
Go to your Storyboard and click on the scroll view's bottom constraint (the blue line that gives the scroll view its bottom constraint of 50). In the Attributes Inspector you should be able to see details about your constraint, it should look something like this
In the field that asks for an Identifier, give it the name "ScrollViewBottom" or whatever name you like.
Now loop over all the constraints that make up your scroll view and use the identifier name to find the correct one and change it's constant as follows
for constraint in yourScrollView.superview!.constraints {
if constraint.identifier == "ScrollViewBottom" {
constraint.constant = 0
}
}
Finally, force the view to take into account of this change by calling the following straight after
view.layoutIfNeeded()

Full width view does not contain same frame width as parent

I'm having an issue where I have a UIImageView that spans the entire width of the screen using the following constraints.
Align Leading to : Superview (Equals: -20)
Align Trailing to : Superview (Equals: -20)
Top Space to : Top Layout Guide (Equals: 0)
Aspect Ratio : 1:1
The image assumes the width of the screen while gaining aspect ratio (in this case a square). The issue comes with when I started attempting to make a circle out of the image, that I noticed that my UIIMageView's frames do not have proper values.
You'll have to excuse to code below for not being in Objective-C as I'm using Java through RoboVM but with very little effort you can mentally convert the code to Objective-C as it's pretty straightforward.
public class MyViewController extends UIViewController {
#IBOutlet private UIImageView myImageView;
#Override public void viewDidLoad() {
System.out.println("Image view width: " + myImageView.getFrame().getWidth());
System.out.println("Superview width: " + this.getView().getFrame().getWidth());
}
}
The results of running the application with this Controller shows the following:
Image view width: 240.0
Superview width: 320.0
When cutting the frame width of the image in half and applying that to the CALayer#cornerRadius property, the image does not make a complete circle, however when using half of the superviews width (Which is actually the size of the image) the result is a perfect circle.
In viewDidLoad, it has just loaded your view from the XIB or storyboard, not sized it yet. The size should be correct when viewWillAppear is called. So try putting your code in viewWillAppear and see if that helps.
The reason you need the -20 margin is because your superview has a margin of 20, and the constraints are created by default to 'Relative to margin' - if you uncheck that, you can set the 'real' constraint - in this case zero rather than -20
If you select the constraint in the assistant editor, you will see the check box to toggle this value

issues with UIImageView.layer.cornerRadius to create rounded images on different pixel densities ios

I'm simply trying to create a perfectly round image. Here's my swift code:
myImage.layer.cornerRadius = myImage.frame.size.width/2
myImage.layer.masksToBounds = true
This works on a 4s, but is not quite round on a 5s, and appears as a rounded rectangle on a iphone 6.
I'm assuming this has to do with frame.size.width returning values in pixels not points or something like that, but I've been unable to solve this problem.
If you're putting that code in viewDidLoad, try moving it to viewDidLayoutSubviews.
I'm guessing it's an auto layout issue -- although you've used the corner radius property appropriately and are in fact making the image frame circular, after auto-layout, the corner radius stays the same, but the image stretches so that it's no longer circular.
If your code is in viewDidLoad in ViewController, try moving it to viewDidLayoutSubviews.
If your rounded imageView is in tableViewCell, try moving it to draw.
override func draw(_ rect: CGRect) {
avatarView.layer.cornerRadius = avatarView.frame.size.width / 2
avatarView.layer.masksToBounds = true
avatarView.clipsToBounds = true
avatarView.contentMode = .scaleAspectFill
}
The problem is that if you change the cornerRadius of Any view, and expect it to look like a circle, the view has to be a square.
Now, because of different devices and different device size, the size of your image view might change and it may be a rectangle.
For e.g.
If you view is a 50 * 50
myImage.layer.cornerRadius = myImage.frame.size.width/2
This would add corner radios of 25 on both sides to make it a circle.
But because of auto layout of device change, your view might be a 50 * 80
Corner radius would add a 25, but as the height is 80, it will add 25 on both sides, and the remaining 30 won't be a curve and look straight.
What you can do is try viewing the screen in various orientations in the storyboard and change auto layout Constraints (Or structs and springs) to ensure that the image view is a square in all the devices
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myImage.layer.cornerRadius = myImage.frame.size.width/2
myImage.layer.masksToBounds = true
}
This should work:
myImage.layer.cornerRadius = myImage.**bounds**.size.width/2
myImage.layer.masksToBounds = true

Where to update Autolayout constraints when size changes?

I have several UIViews laid out along the bottom of a containing UIView. I want these views to always be equal width, and always stretch to collectively fill the width of the containing view (like the emoji keyboard buttons at the bottom). The way I'm approaching this is to set equal widths to one of the views, then just update the width constraint of that view to be superviewWidth / numberOfViews which will cause all of the other views to update to that same value.
I am wondering where the code to change the constraint constant needs to go. It needs to be set before the keyboard appears on screen for the first time and update when rotating the device.
My first attempt at a solution was to place it in updateViewConstraints and calculate the width via containerView.frame.size.width. But this method is called twice upon load, the first time it calculates the values correctly, but the second time for some reason the containerView's width is 0.0. Another issue is that when rotating, the containerView's width is not the value that it will be after rotation, it's the current value before rotation. But I don't want to wait until after the rotation completes to update the constraint, because the buttons will be the original size then change which will be jarring to the user.
My question is: where is the most appropriate place to put this code? Is there a better way to calculate what the width will be? I can guarantee it will always be the exact same width as the screen width. And I am using Size Classes in Xcode 6, so willRotateToInterfaceOrientation and similar methods are deprecated.
On all classes that implement the UITraitEnvironment protocol the method traitCollectionDidChange will be called when the trait collection changes, like on rotation. This is the appropiate place to manually update the constraints when using the new Size Classes. You can also animate the transition with the method willTransitionToTraitCollection
Basic example:
class ViewController: UIViewController {
var constraints = [NSLayoutConstraint]()
func updateConstraintsWithTraitCollection(traitCollection: UITraitCollection) {
// Remove old constraints
view.removeConstraints(constraints)
// Create new constraints
}
override func willTransitionToTraitCollection(newCollection: UITraitCollection!,
withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator!) {
super.willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition({ (context: UIViewControllerTransitionCoordinatorContext!) in
self.updateConstraintsWithTraitCollection(newCollection)
self.view.setNeedsLayout()
}, completion: nil)
}
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection!) {
updateConstraintsWithTraitCollection(traitCollection)
}
}
Besides that I want to recommend Cartography, which is a nice library that helps to make auto layout more readable and enjoyable. https://github.com/robb/Cartography
There is no reason to update the width manually:
Place all the views with equal width in your view with no spacing in between each other
Add an equal width constraint to all of them
Add constraints with 0 width for spacing between sides and each other
Lower the priority of one or more of the equal width constraints just in case the width cannot be divided equally.
Then auto layout will handle everything for you.

Resources