You are on page 1of 62

Adaptive


Auto Layout
Silicon Valley iOS Developers' Meetup
March 2015

Tyler Fox
@smileyborg
Layout
What is Layout?
y
x

View height

width

#SViOS Adaptive Auto Layout @smileyborg


Manual Layout

view.frame = CGRect(x: x,
y: y,
width: width,
height: height)

#SViOS Adaptive Auto Layout @smileyborg


Auto Layout

view.frame

an output calculated by the auto layout engine


based on your constraints

#SViOS Adaptive Auto Layout @smileyborg


Living in an
Adaptive World
http://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions
#SViOS Adaptive Auto Layout @smileyborg
Size Classes

enum UIUserInterfaceSizeClass : Int {


case Unspecified
case Compact
case Regular
}

#SViOS Adaptive Auto Layout @smileyborg


Size Classes
enum UIUserInterfaceSizeClass : Int {
case Unspecified
case Compact
case Regular
}

Regular
Regular

iPad iPad
Regular Regular
Portrait Landscape

#SViOS Adaptive Auto Layout @smileyborg


Size Classes
enum UIUserInterfaceSizeClass : Int {
case Unspecified
case Compact
case Regular
}

Compact
Compact

iPhone iPhone
Regular Compact
Portrait Landscape

#SViOS Adaptive Auto Layout @smileyborg


Size Classes
enum UIUserInterfaceSizeClass : Int {
case Unspecified
case Compact
case Regular
}

Compact
Regular
iPhone iPhone
Regular 6 Plus Compact 6 Plus
Portrait Landscape

#SViOS Adaptive Auto Layout @smileyborg


Traits

UITraitCollection

horizontalSizeClass Compact

verticalSizeClass Regular

#SViOS Adaptive Auto Layout @smileyborg


Traits
<UITraitEnvironment>
UITraitCollection

UIScreen horizontalSizeClass Compact


UIWindow
UIViewController
UIView
verticalSizeClass Regular

* UIPresentationController also conforms to UITraitEnvironment


#SViOS Adaptive Auto Layout @smileyborg
Trait Environments
UIScreen

UIWindow
ChildVC1 UIWindow
UIScreen
ParentVC ChildVC2
ParentView
ChildView1 ChildView2

UIViewController

UIView

#SViOS Adaptive Auto Layout @smileyborg


UIScreen
UIScreen

UIWindow
UIWindow

ParentVC

UIViewController

ChildVC1 ChildVC2

ParentView

UIView
ChildView1 ChildView2

#SViOS Adaptive Auto Layout @smileyborg


Responding to Changes
UITraitEnvironment Screens, Windows, View Controllers, Views

var traitCollection: UITraitCollection { get }

func traitCollectionDidChange(previousTraitCollection: UITraitCollection?)

UIContentContainer View Controllers

func willTransitionToTraitCollection(newCollection: UITraitCollection,


withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

func viewWillTransitionToSize(size: CGSize,


withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

* UIPresentationController conforms to both


#SViOS Adaptive Auto Layout @smileyborg
Rotation
iPad
768 Regular

1024 Portrait Regular

Change in size Same size classes

768 Landscape Regular

1024 Regular

#SViOS Adaptive Auto Layout @smileyborg


Rotation
iPhone 6 Plus
414 Compact

736 Portrait Regular

Change in size Change in size classes

414 Landscape Compact

736 Regular

#SViOS Adaptive Auto Layout @smileyborg


Handling Rotation

Dep
-[willRotateToInterfaceOrientation:duration:]

reca
-[didRotateFromInterfaceOrientation:]

self.interfaceOrientation

ted
-[willAnimateRotationToInterfaceOrientation:duration:]

#SViOS Adaptive Auto Layout @smileyborg


Handling Rotation in iOS 8
override func viewWillTransitionToSize(size: CGSize,
withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

// Code here will execute before the rotation begins.


// Equivalent to placing it in the deprecated method -[willRotateToInterfaceOrientation:duration:]

coordinator.animateAlongsideTransition({ (context) -> Void in

// Place code here to perform animations during the rotation.


// You can pass nil for this closure if not necessary.

},
completion: { (context) -> Void in

// Code here will execute after the rotation has finished.


// Equivalent to placing it in the deprecated method -[didRotateFromInterfaceOrientation:]

})
}

#SViOS Adaptive Auto Layout @smileyborg


Migrating Legacy Code
typedef NS_ENUM(NSInteger, ViewOrientation) {
ViewOrientationPortrait,
ViewOrientationLandscape
};

@interface UIView (Orientation)

/** Returns the "orientation" of size.


width > height is considered "landscape", otherwise "portrait" */
+ (ViewOrientation)viewOrientationForSize:(CGSize)size;

/** Returns the "orientation" of this view based on its size.


width > height is considered "landscape", otherwise "portrait" */
- (ViewOrientation)viewOrientation;

/** Returns YES if this view's height >= width */


- (BOOL)isViewOrientationPortrait;
/** Returns YES if this view's width > height */
- (BOOL)isViewOrientationLandscape;

@end

GitHub Gist
bit.ly/1BHOYoF

#SViOS Adaptive Auto Layout @smileyborg


Making Layout Decisions
✅ What are my size classes?

✅ What is my size?

🚫 What is the status bar orientation?

🚫 What is the device or screen size?

🚫 What traits or sizes do my ancestors have?

#SViOS Adaptive Auto Layout @smileyborg


Interface Builder
Auto Layout
Size Class Controls

#SViOS Adaptive Auto Layout @smileyborg


Size Class Controls

#SViOS Adaptive Auto Layout @smileyborg


Size Class Controls

#SViOS Adaptive Auto Layout @smileyborg


Placeholder Constraints

#SViOS Adaptive Auto Layout @smileyborg


Uninstalled Placeholder
Constraint Constraint
Uses • Customize constraint for • Silence compile-time IB
specific size classes layout warnings
• Allow easy runtime • Prevent IB from injecting
switching between constraints to fix ambiguous
constraints layout

Lifespan • Exists at runtime (but • Exists only in IB, never


inactive) created at runtime

IBOutlets • Can reference via outlets • No, runtime crash

Layout • Does not affect IB layout • Factors into IB layout


Checking checking checking

#SViOS Adaptive Auto Layout @smileyborg


Coding
Auto Layout
NSLayoutConstraint
view1.setTranslatesAutoresizingMaskIntoConstraints(false);
view2.setTranslatesAutoresizingMaskIntoConstraints(false);
let c = NSLayoutConstraint(item: view1,
attribute: .Left,
relatedBy: .GreaterThanOrEqual,
toItem: view2,
attribute: .Right,
multiplier: 1.0,
constant: 10.0)
c.priority = 750
c.active = true

#SViOS Adaptive Auto Layout @smileyborg


VFL
view1.setTranslatesAutoresizingMaskIntoConstraints(false);
view2.setTranslatesAutoresizingMaskIntoConstraints(false);
let views = ["view1": view1,
"view2": view2]
let metrics = ["spacing": 10.0,
"priority": 750]
let c = NSLayoutConstraint.constraintsWithVisualFormat(
"H:[view2]-(>=spacing@priority)-[view1]",
options: NSLayoutFormatOptions(0),
metrics: metrics,
views: views)
NSLayoutConstraint.activateConstraints(c)

#SViOS Adaptive Auto Layout @smileyborg


PureLayout

UIView.autoSetPriority(750) {
view1.autoPinEdge(.Left,
toEdge: .Right,
ofView: view2,
withOffset: 10.0,
relation: .GreaterThanOrEqual)
}

#SViOS Adaptive Auto Layout @smileyborg


PureLayout

The missing API


for Auto Layout

github.com/smileyborg/PureLayout

#SViOS Adaptive Auto Layout @smileyborg


PureLayout
• Complete API for Auto Layout
• Designed to be consistent with Apple's API style
• Balances simplicity and power
• Engineered to minimize third-party code
• Supports mixing and matching with other approaches
• Fully compatible with Swift & Objective-C
• Supports both iOS (6.0+) and OS X (10.7+)

#SViOS Adaptive Auto Layout @smileyborg


Adaptive
Layout Code
Idempotent

Produces the same results


when executed once or many times

#SViOS Adaptive Auto Layout @smileyborg


Idempotent
Produces the same results
when executed once or many times


func abs(x: Int) -> Int {
return (x < 0) ? -x : x
}

#SViOS Adaptive Auto Layout @smileyborg


Idempotent
Produces the same results
when executed once or many times

🚫
func purchaseItem(item: Item) {
charge(creditCard, forAmount: item.price)
}

#SViOS Adaptive Auto Layout @smileyborg


Idempotent
Produces the same results
when executed once or many times


func emptyCart() {
shoppingCart.removeAllItems()
}

#SViOS Adaptive Auto Layout @smileyborg


Idempotent Layout
enum Layout {
case A
case B
}

func layoutForCurrentState() -> Layout {


switch (traitCollection.horizontalSizeClass) {
case .Compact:
return .A
case .Regular:
fallthrough
default:
return .B
}
}

#SViOS Adaptive Auto Layout @smileyborg


Idempotent Layout
var layoutAConstraints: NSArray?
var layoutBConstraints: NSArray?

override func updateConstraints() {


switch (layoutForCurrentState()) {
case .A:
if (layoutAConstraints == nil) {
layoutBConstraints?.autoRemoveConstraints()
layoutBConstraints = nil
layoutAConstraints = setupConstraintsForLayoutA()
}
case .B:
if (layoutBConstraints == nil) {
layoutAConstraints?.autoRemoveConstraints()
layoutAConstraints = nil
layoutBConstraints = setupConstraintsForLayoutB()
}
}
super.updateConstraints()
}

#SViOS Adaptive Auto Layout @smileyborg


Idempotent Layout
func setupConstraintsForLayoutB() -> NSArray {
let constraints: NSArray = UIView.autoCreateConstraintsWithoutInstalling() {
self.view1.autoPinEdgesToSuperviewMarginsExcludingEdge(.Bottom)
self.view2.autoPinEdge(.Top, toEdge: .Bottom, ofView: self.view1)
self.view2.autoPinEdgesToSuperviewMarginsExcludingEdge(.Top)
}
constraints.autoInstallConstraints()
return constraints
}

#SViOS Adaptive Auto Layout @smileyborg


Modifying Constraints

✅ Efficient
Changing the constant of installed constraints

⚠️ Less efficient
Removing existing constraints

and installing new constraints

#SViOS Adaptive Auto Layout @smileyborg


Other Performance
Considerations

Activating optional constraints



is more expensive than activating

Required-priority constraints

Activating multiple constraints at once



can be more efficient than one-by-one

#SViOS Adaptive Auto Layout @smileyborg


Managing the
Complexity
Composition

View hierarchy is a tree of subviews.


Manage your layout in a similar way.

Table and collection views inherently model this,


as they are composed of cells!

#SViOS Adaptive Auto Layout @smileyborg


#SViOS Adaptive Auto Layout @smileyborg
#SViOS Adaptive Auto Layout @smileyborg
#SViOS Adaptive Auto Layout @smileyborg
Debugging
Auto Layout
System Generated
Constraints

NSAutoresizingMaskLayoutConstraint
Constraints generated for views not using auto layout

NSIBPrototypingLayoutConstraint
Constraints generated by Interface Builder to fix layout ambiguity

#SViOS Adaptive Auto Layout @smileyborg


Constraint Identifiers
let constraint: NSLayoutConstraint = /* ... */
constraint.identifier = "A helpful description of my constraint"

view2.autoPinEdge(.Top,
toEdge: .Bottom,
ofView: view1).autoIdentify("view2.top = view1.bottom")

UIView.autoSetIdentifier("Pin to Superview Constraints") {


self.view1.autoPinEdgesToSuperviewMarginsExcludingEdge(.Bottom)
self.view2.autoPinEdgesToSuperviewMarginsExcludingEdge(.Top)
}

#SViOS Adaptive Auto Layout @smileyborg


Runtime View Inspector

#SViOS Adaptive Auto Layout @smileyborg


UIView.h Debugging
Warning: For debugging only!
Do not use these APIs in shipping code.

@interface UIView (UIConstraintBasedLayoutDebugging)

- (NSArray *)constraintsAffectingLayoutForAxis:(UILayoutConstraintAxis)axis;

- (BOOL)hasAmbiguousLayout;
- (void)exerciseAmbiguityInLayout;

@end

#SViOS Adaptive Auto Layout @smileyborg


Advanced
Auto Layout
Harnessing the Power

Constraints
Auto Layout View
Engine Frames

Use offscreen views and constraints to


calculate frames for onscreen views

#SViOS Adaptive Auto Layout @smileyborg


#SViOS Adaptive Auto Layout @smileyborg
Offscreen Layout

#SViOS Adaptive Auto Layout @smileyborg


Offscreen Layout

New
startFrame:
CGRect

#SViOS Adaptive Auto Layout @smileyborg


Offscreen Layout

startFrame:
CGRect

New
endFrame:
CGRect

#SViOS Adaptive Auto Layout @smileyborg


Offscreen Layout

Grab the code on GitHub


github.com/smileyborg/AdaptiveAutoLayout

#SViOS Adaptive Auto Layout @smileyborg


Tyler Fox

@smileyborg

linkedin.com/in/smileyborg

github.com/smileyborg