Autolayout with Anchors in swift
To make your app beautiful in any screen and orientation is a challenging task. Autolayout make it easy to support different screen sizes in your apps. First will try to understand the autolayout fundamental before learning programatic layouts. Click here to know about autolayout fundamental.
There are two ways you can write autolayout in code.
- Using Auto Layout Visual Format Language.
- Using Layout Anchors.
In this blog, I’l try to cover most of the things about Anchors and how to debug the layout error.
Steps need to follow to add constraints:-
- Call
addSubview
so that view is in the hierarchy. - Set
translatesAutoresizingMaskIntoConstraints = false
- Create constraints and set
isActive = true
to enable constraints. - When activating or deactivating lots of constraints at once, it’s more efficient to pass them as an array to
activate()
anddeactivate()
respectively.
E.g :- Create a view with fixed width, height and it is horizontally centre aligned.
- Create a view with background colour
let redView = UIView()
redView.backgroundColor = UIColor.red
2. Set translatesAutoresizingMaskIntoConstraints = false
redView.translatesAutoresizingMaskIntoConstraints = false
3. Add redview as subview to parent view so that redview is in the view hierarchy
view.addSubview(redView)
4. Set Width Constraint for the view
redView.widthAnchor.constraint(equalToConstant: 100).isActive = true
3. Set Height Constraint
redView.heightAnchor.constraint(equalToConstant: 100).isActive = true
4. or you can enable all the required constraints by passing them as an array to activate()
NSLayoutConstraint.activate([
redView.widthAnchor.constraint(equalToConstant: 100),
redView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50),
redView.heightAnchor.constraint(equalTo: redView.widthAnchor, multiplier: 1),
redView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
5. finally your view look like this:
API’s available for setting Constraints:-
The cool thing about NSLayoutAnchor
is its generic constrained API design. Constraints are divided into X-axis, Y-axis and dimension anchor type that makes it hard to make mistakes.
open class NSLayoutXAxisAnchor : NSLayoutAnchor<NSLayoutXAxisAnchor>
open class NSLayoutDimension : NSLayoutAnchor<NSLayoutDimension>
open class NSLayoutYAxisAnchor : NSLayoutAnchor<NSLayoutYAxisAnchor>
- Use bellow methods for setting system spacing between to views
// for y axis
1. constraint(equalToSystemSpacingBelow anchor: NSLayoutYAxisAnchor, multiplier: CGFloat) -> NSLayoutConstraint
2. constraint(greaterThanOrEqualToSystemSpacingBelow anchor: NSLayoutYAxisAnchor, multiplier: CGFloat) -> NSLayoutConstraint
3. constraint(lessThanOrEqualToSystemSpacingBelow anchor: NSLayoutYAxisAnchor, multiplier: CGFloat) -> NSLayoutConstraint// for x axis
4. constraint(equalToSystemSpacingAfter anchor: NSLayoutXAxisAnchor, multiplier: CGFloat) -> NSLayoutConstraint5. constraint(greaterThanOrEqualToSystemSpacingAfter anchor: NSLayoutXAxisAnchor, multiplier: CGFloat) -> NSLayoutConstraint6. constraint(lessThanOrEqualToSystemSpacingAfter anchor: NSLayoutXAxisAnchor, multiplier: CGFloat) -> NSLayoutConstraint
e.g: redView.topAnchor.constraint(equalToSystemSpacingBelow: view.topAnchor, multiplier: 1)
2. Use bellow methods for setting custom spacing between to views
//for width and hight
1. constraint(equalTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat) -> NSLayoutConstraint
2. constraint(greaterThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat) -> NSLayoutConstraint
3. constraint(lessThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat) -> NSLayoutConstraint// for x and y axis
4. constraint(equalTo anchor: NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint
5. constraint(greaterThanOrEqualTo anchor: NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint
6. constraint(lessThanOrEqualTo anchor: NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint
e.g: redView.topAnchor.constraint(equalTo: view.topAnchor)
How can you set priority for constraints?
You can set priority for any constraint using UILayoutPriority of range 1 to 1000. Click here to know about autolayout fundamental and view priority
// added priority for width constraint
let widthConstrain = redView.widthAnchor.constraint(equalToConstant: 100)
widthConstrain.priority = UILayoutPriority(rawValue: 999)
widthConstrain.isActive = true
What is aspect ratio and How can you set this?
- Aspect ratio describes the ratio between the width and height of a view or screen.
- A 1:1 aspect ratio, for example, is a square. The first number always refers to the width, and the second number refers to the height
- You can set this property using multiplier.
//1:2 i.e view height is 2 times greater than it's width
redView.heightAnchor.constraint(equalTo: redView.widthAnchor, multiplier: 2)
How can we set content hugging and compression resistance?
Click here to know about autolayout fundamental and CHCR
//setting Content Hugging Priority horizontally
redView.setContentHuggingPriority(UILayoutPriority(rawValue: 999), for: .horizontal)//setting Content Hugging Priority vertically
redView.setContentHuggingPriority(UILayoutPriority(rawValue: 999), for: .vertical)//setting Compression Resistance Priority horizontally
redView.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 999), for: .horizontal)//setting Compression Resistance Priority vertically
redView.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 999), for: .vertical)
Available layout guides:-
Each UIView
comes with a few layout guides which can also be used as anchors.
- LayoutMarginsGuide
- ReadableContentGuide
- SafeAreaLayoutGuide
layoutMarginsGuide:
- Set constraints and keep the layout margins as spacing
/*
- It adds default UIEdgeInsets for the layout margin guide.
- If you want to change you can add additional edge insets by - view.layoutMargins = UIEdgeInsets(top: 150, left: 50, bottom: 50, right: 50)- In iOS 11 and later, use the directionalLayoutMargins to define layoutMargins as:
view.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 150, leading: 50, bottom: 50, trailing: 50)
*/// LayoutMarginsGuide for UIView
redView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor)
- Constraints the width to a size that is easy for the user to read. Want to know more about this?Read this blog.
//ReadableContentGuide for UIViewredView.widthAnchor.constraint(equalTo: view.readableContentGuide.widthAnchor)
SafeAreaLayoutGuide:
- Represents the portion of your view that is unobscured by bars and other content.
- Apple has deprecated
topLayoutGuide
&bottomLayoutGuide
. So instead of having two dummy boxes, you now have one dummy box namedsafeAreaLayoutGuide
on the UIView instance. - Want to know more about Safe Area? Read this excellent post by Evgeny Mikhaylov.
//SafeAreaLayoutGuide for UIView
redView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor)// you can set the additionally safe area insets to view controller as:
viewController.additionalSafeAreaInsets = UIEdgeInsets(top: 150, left: 50, bottom: 50, right: 50)
Some of common error found while coding layouts:-
For example, we can’t pin the top anchor to the left anchor as they are in different axis, and it makes no sense. Attempt to do the following results in compilation issue as Swift strong type system ensures correctness.
- Bellow error is because of setting redView.topAnchor to view.leftAnchor. It can be fixed by changing view.leftAnchor to NSLayoutYAxisAnchor like view.topAnchor or view.bottomAnchor.
2. This error is causing because of adding Int value but function expecting NSLayoutAnchor object. It can be resolved by changing from equalTo
to equalToConstant
3. Constraints break:- If you add both top constraint and centerY constraint to redView with higher priority. It can be resolved by removing one of the conflict constraint or set one of the constraint to lower priority.
redView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
redView.topAnchor.constraint(equalTo: view.centerYAnchor)
Animate changing Auto Layout constraints:-
UIView.animate(withDuration: 1.0) { [weak self] in
self?.imageToTopConstraint.isActive = false
self?.view.layoutIfNeeded()
}
External dependency when writing constraints in code:-
There are a lot of external dependencies that say that they make it easier to write constraints. I hope that the above examples showed that it’s already quite easy to write constraints in code by making use of the default APIs. External frameworks introduce custom code patterns that might not be familiar to current and future colleagues.
Therefore, I would recommend you to make use of Layout Anchors and stick with Apple’s default APIs.
Debugging Auto Layout constraints:-
- Constraints break can be easily identified by using identifier. It can be set as
constraint.identifier = identifier name
let widthConstraint = redView.widthAnchor.constraint(equalToConstant: 100)// setting identifier to the constraint.
widthConstraint.identifier = "redView width constraint"widthConstraint.isActive = true
- You identify the Constraint Conflicts using these identifier. Now log look like this,
Thanks for reading! hit the clap button below 👏 to help others find it!. follow me on Medium.