How to make Auto Layout more convenient in iOS

Khoa Pham
Khoa Pham
Apr 25 · 15 min read
Thanks to unDraw for great illustrations!

Positioning a view before Auto Layout

Manual layout using CGRect

Why is view size correct in viewDidLoad

Autoresizing masks

box.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]

Auto Layout to the rescue

A constraint-based layout system

item1.attribute1 = multiplier × item2.attribute2 + constant

translatesAutoresizingMaskIntoConstraints

[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSAutoresizingMaskLayoutConstraint:0x600003ef2300 h=--& v=--& UIView:0x7fb66c5059f0.midX == 0 (active)>",
"<NSLayoutConstraint:0x600003e94b90 H:|-(20)-[UIView:0x7fb66c5059f0](LTR) (active, names: '|':UIView:0x7fb66c50bce0 )>"
)
box.translatesAutoresizingMaskIntoConstraints = false

Visual Format Language

addConstraint and activate

NSLayoutAnchor

NSLayoutConstraint.activate([
box.topAnchor.constraint(equalTo: view.topAnchor, constant: 50),
box.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20),
box.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20),
box.heightAnchor.constraint(equalToConstant: 100)
])
open class NSLayoutXAxisAnchor : NSLayoutAnchor<NSLayoutXAxisAnchor>
open class NSLayoutDimension : NSLayoutAnchor<NSLayoutDimension>
box.topAnchor.constraint(equalTo: view.leftAnchor, constant: 50),

Ambiguous error message with NSLayoutAnchor

NSLayoutConstraint.activate([
box.topAnchor.constraint(equalTo: view.centerXAnchor, constant: 50)
])
Value of optional type 'UIView?' must be unwrapped to refer to member 'centerXAnchor' of wrapped base type 'UIView'
NSLayoutConstraint.activate([
imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
imageView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8),
imageView.heightAnchor.constraint(equalToConstant: view.heightAnchor, mult0.7),
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: 1.0)
])

Abstractions over Auto Layout

Cartography

constrain(button1, button2) { button1, button2 in
button1.right == button2.left - 12
}
constrain(logoImnageView, view1, view2) { logoImageView, view1, view2 in
logoImageView.with == 74
view1.left == view2.left + 20
}
Constraint.on(
logoImageView.widthAnchor.constraint(equalToConstant: 74),
view1.leftAnchor.constraint(equalTo: view2.leftAnchor, constant: 20),
)

SnapKit

box.snp.makeConstraints { (make) -> Void in
make.width.height.equalTo(50)
make.center.equalTo(self.view)
}
https://github.com/onmyway133/blog/issues/22
keyB.snp.makeConstraints { (make) -> Void in
make.left.equalTo(self.keyA.right)
}
keyC.snp.makeConstraints { (make) -> Void in
make.left.equalTo(self.keyB.right)
}
keyD.snp.makeConstraints { (make) -> Void in
make.left.equalTo(self.keyC.right)
}

The many overloading functions

box.pinEdgesToSuperview()
box.pinEdgesToView(_ view: UIView)
box.pinEdgesToView(_ view: UIView, insets: UIEdgeInsets)
box.pinEdgesToView(_ view: UIView, insets: UIEdgeInsets, exclude: NSLayoutConstraint.Attribute)
box.pinEdgesToView(_ view: UIView, insets: UIEdgeInsets, exclude: NSLayoutConstraint.Attribute, priority: NSLayoutConstraint.Priority)

Embracing Auto Layout

NSLayoutConstraint.on([
box.pinCenter(view: view),
box.pin(size: CGSize(width: 100, height: 50))
])
let topConstraint = box.topAnchor.constraint(equalTo: view.topAnchor, constant: 50)
topConstraint.priority = UILayoutPriority.defaultLow
NSLayoutConstraint.on([
topConstraint
])

Making Auto Layout more convenient with the builder pattern

activate(
boxA.anchor.top.left,
boxB.anchor.top.right,
boxC.anchor.bottom.left,
boxD.anchor.bottom.right
)

Which objects can interact with Auto Layout?

public class Anchor: ConstraintProducer {
let item: AnyObject
/// Init with View
convenience init(view: View) {
self.init(item: view)
}
/// Init with Layout Guide
convenience init(layoutGuide: LayoutGuide) {
self.init(item: layoutGuide)
}
// Init with Item
public init(item: AnyObject) {
self.item = item
}
}
public extension View {
var anchor: Anchor {
return Anchor(view: self)
}
}
public extension LayoutGuide {
var anchor: Anchor {
return Anchor(layoutGuide: self)
}
}

Which properties are needed in a layout constraint?

func paddingHorizontally(_ value: CGFloat) -> Anchor {
removeIfAny(.leading)
removeIfAny(.trailing
pins.append(Pin(.leading, constant: value))
pins.append(Pin(.trailing, constant: -value)
return self
}

Inferring constraints

box.anchor.width.constant(10)
box.anchor.height.ratio(2) // height==width*2
if sourceAnchor.exists(.width) {
return Anchor(item: sourceAnchor.item).width
.equal
.to(Anchor(item: sourceAnchor.item).height)
.multiplier(ratio).constraints()
} else if sourceAnchor.exists(.height) {
return Anchor(item: sourceAnchor.item).height
.equal
.to(Anchor(item: sourceAnchor.item).width)
.multiplier(ratio).constraints()
} else {
return []
}

Retrieving a constraint

boxA.anchor.find(.height)?.constant = 100// later
boxB.anchor.find(.height)?.constant = 100
// later
boxC.anchor.find(.height)?.constant = 100

How to reset constraints

constrain(view, replace: group) { view in
view.top == view.superview!.top
view.left == view.superview!.left
}
let g1 = group(box.anchor.top.left)
let g2 = group(box.anchor.top.right)
let g3 = group(box.anchor.bottom.right)
let g4 = group(box.anchor.bottom.left)

Where to go from here

https://github.com/onmyway133/Anchors
activate(
lineBlock.anchor.left.bottom
)
// later
activate(
firstSquareBlock.anchor.left.equal.to(lineBlock.anchor.right),
firstSquareBlock.anchor.bottom
)
// later
activate(
secondSquareBlock.anchor.right.bottom
)

Flawless iOS

🍏 Community around iOS development, mobile design, and marketing

Thanks to Lisa Dziuba.

Khoa Pham

Written by

Khoa Pham

Cageball apprentice at @hyperoslo. My apps https://onmyway133.github.io/

Flawless iOS

🍏 Community around iOS development, mobile design, and marketing