Auto Layout in iOS UIKit

Ajaya Mati
8 min readJul 23, 2023
Photo by Seyi Ariyo on Unsplash

Auto Layout is a constraint-based layout system that automatically positions and sizes view in a user interface, as a result, allows us to create a dynamic/responsive UI for different screen sizes and orientations.

First, let’s talk about creating UI without Auto Layout.

Frame-based Layout system

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.

let view = UIView(frame: .init(x: 100, y: 500, width: 200, height: 300))
view.backgroundColor = .red

self.view.addSubview(view)
}
}

Here we are creating a UIView named view and giving the frame as CGRect(x: 100, y: 500, width: 200, height: 300) and then adding the view as a subview to the ViewController’s view. So what can go wrong?

Our layout looks okay in iPhone 14 pro but when it comes to smaller size devices like iPhone SE the UI gets clipped. Also if we change the orientation to landscape in iPhone 14 pro, you won't be able to see the red box. Let’s see what is happening here.

Here the View Controller’s view takes the whole device frame i.e (393,852) Our custom UIView then gets added into the bound of the View Controller’s view, So the UI is static here.

Frame refers to the coordinate system of the view’s container (parent view). Bounds refer to the own coordinate system of the view.

But is there any way to solve this?

Yes. Consider, We want the red box to be positioned at the bottom of the screen with a gap of 16 points vertically, and horizontally center to the device screen. Here’s how we can do.

// Width and Height of the Red Box
let width: CGFloat = 200.0
let height: CGFloat = 300.0

///Calculating `x-position`
/// View is horizontaly centered to the super view
let x: CGFloat = self.view.center.x - (width / 2.0)

/// Calculating `y-position`
/// View is verticaly placed at the bottom with a gap of 16 Point
let y: CGFloat = self.view.frame.maxY - 16.0 - height

let view = UIView(frame: .init(x: x, y: y, width: width, height: height))
view.backgroundColor = .red

self.view.addSubview(view)

The above code will work just fine with any device size and orientation. But, creating a complex layout with 100s of views where we have to calculate the frame of every view would be an impossible task.

Now you know the issue with the frame-based layout system and here, Auto Layout, the constraint-based layout system comes into play.

Constraint-based layout system

Constraints are Linear Equations that we provide to a UIView and using these constraints the frame of the UIView is then gets calculated in the run time.

The equation follows the format

item1.attribute1 = multiplier × item2.attribute2 + constant

Let’s replicate the Red Box layout with Constraints.

Using Storyboard to Add Constraints

We have added four constraints to our Red Box with the help of the storyboard.

If you expand the bottom constraint you’ll see this.

=> Safe Area.Bottom = Red Box.Bottom * Multiplier + Constant

Multiplier = 1, So
=> Safe Area.Bottom = Red Box.Bottom + Constant
=> Red Box.Bottom = Safe Area.Bottom - Constant

The above equation is how the frame of the red box gets calculated. Likewise, we can express other constraints into equations.

Programmatically adding Constraints

Constraints are instances of NSLayoutConstraint and can be made directly with a specified initializer or using NSLayoutAnchor .

The initializer is defined as

convenience init(
item view1: Any,
attribute attr1: NSLayoutConstraint.Attribute,
relatedBy relation: NSLayoutConstraint.Relation,
toItem view2: Any?,
attribute attr2: NSLayoutConstraint.Attribute,
multiplier: CGFloat,
constant c: CGFloat
)

From Apple:
Constraints represent linear equations of the form view1.attr1 <relation> multiplier × view2.attr2 + c. If the constraint you wish to express does not have a second view and attribute, use nil and NSLayoutConstraint.Attribute.notAnAttribute.

We created the four constraints for width, height, centerX, and bottom, the same as the constraints we created earlier using the storyboard. After creating the NSLayoutConstrains we have to activate them. We can individually activate each constraint by setting theisActive property to true , or using activate(_ constraints: [NSLayoutConstraint]) .

Here we have to explicilty set translatesAutoresizingMaskIntoConstraints set to false, it means that the autoresizing mask is not translated into constraints, and you are indicating that you'll be using Auto Layout to define the view's layout constraints explicitly. For more insight checkout this video.

We used safeAreaLayoutGuide of the super view to make constraints with the safe area instead of the view directly.

From Apple:
This method throws an invalidArgumentException exception if it is used to create an invalid constraint (for example, view1.top == 0.0 x nil.NotAnAttribute + 200.0 or view1.top == 1.0 x view2.height + 20.0).

Using Anchor to add constraints is a bit easy, and better error-proof.

We can replicate the same constraints using NSLayoutAnchor , and here’s how we are going to do it.

// Dimensional Constraints
let widthConstraint = redBoxView.widthAnchor.constraint(equalToConstant: 200.0)
let heightConstraint = redBoxView.heightAnchor.constraint(equalToConstant: 300.0)

//Axial Contraints
let centerXConstraint = redBoxView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0.0)
let bottomConstraint = redBoxView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -16.0)

In the runtime, we can disable the constraints by setting isActive to false. Also, we can change other properties of NSLayoutConstraint .

Changing constraints inside UIView.animate will create animations.

  UIView.animate(withDuration: 4.0, delay: .zero) {
heightConstraint.constant = 500
self.view.layoutIfNeeded()
}

The above code will create an animation with an expanding height of the Red Box.

By calling layoutIfNeeded(), you force an immediate update of the view's layout, ensuring that any pending layout changes are applied. This is useful in situations where you want to ensure that the view appears correctly with the most up-to-date layout before performing additional operations or animations.

Intrinsic size and Content Hugging Priority vs Content Compression Resistance Priority

UIView components that have intrinsic size are UILabel, UIButton, UIImageView,UITextField, and UITextView . Views with intrinsic size can be used in Auto Layout without specifying their size explicitly, as they can “intrinsically” calculate their width and height. By setting the values for content hugging priority and content compression resistance priority, we can determine how a layout will look when there is a lack of space or extra space is available.

  1. Content Hugging Priority
    Content Hugging Priority is a constraint property that affects a view’s ability to “hug” its content tightly. It determines how much a view resists growing larger than its intrinsic content size when there is extra space available in the layout. Higher Content Hugging Priority values indicate a stronger preference for maintaining the view’s intrinsic size.
  2. Content Compression Resistance Priority
    It determines how much a view resists being compressed when there is a lack of space within the layout. Higher Content Compression Resistance Priority values indicate a stronger preference for maintaining the view’s size and avoiding compression. When there is insufficient space, views with higher Content Compression Resistance Priority are less likely to be resized or truncated. Instead, other views with lower resistance priorities might be resized or compressed first.

Let’s see these two in action.

In the above example, we added two UILabels inside a UIView. For better visualization, I’ve given background colors to each view. Well, the Labels do look fine in the layout but if we change the content of UILabels to longer text they will start overlapping each other. Xcode has given a warning for that so we better give them the required constraints.

We have provided the Red Label with 3 constraints.

  1. Top Constraint with a gap of 16 to the super view
  2. Leading Constraint with a gap of 16 to the super view
  3. Trailing Constraint with a gap of 16 to the Green Label

Similarly, I added 2 more constraints to the Green Label.

  1. Top Constraint with a gap of 16 to the super view
  2. Trailing Constraint with a gap of 16 to the super view

Now each label has 3 constraints (1 common constraint for horizontal gap).

We got an error that says we need to set Horizontal Hugging Priority. But wait, before we set the hugging priority let's set the width of the gray UIView to 2*42(size of the labels)+3*16(gap size) = 132.

Check out the image. The error just disappears. It happened as the labels get width as much as their intrinsic size. There’s no extra space neither lack of space. If we add some extra text to one of the labels we’ll be back with another error.

Now it says we need to set the Horizontal Compression Resistance Priority.

  1. When the frame size was bigger we had extra space so we were getting errors for Horizontal Hugging Priority.
  2. When the frame size is small and the intrinsic size is bigger, we are left with a lack of space, so we got errors for Horizontal Compression Resistance Priority.

In the first scenario, If we change the Horizontal Hugging Priority of the Red Label to 252 keeping 251 for the Green Label, we can see the Green Label expands to the available space but the Red Label keeps its width the same as its intrinsic size. Or we can say that the Red Label is tightly hugging its content.

In the second scenario, we changed the Horizontal Compression Resistance Priority of the Red Label to 751, keeping 750 for Green Label. The Green Label gets trimmed instead of the Red Label because The Red Label has a higher Compression Resistance than the Green Label.

P.S. These two properties have no effect on views that don't have any intrinsic size.

Reference

  1. Auto Resizing Mask:
    https://www.advancedswift.com/autolayout-vs-autoresizing-masks/ https://www.youtube.com/watch?v=pI-yolo9NrM
  2. More On NSLayoutConstraint: https://www.hackingwithswift.com/articles/140/the-auto-layout-cheat-sheet

--

--

Ajaya Mati

iOS Engineer@PhonePe, IITR@2022 | Swift, UIKit, Swift UI