EasyPeasy is a Swift framework that lets you create Auto Layout constraints
programmatically without headaches and never ending boilerplate code. Besides the
basics, EasyPeasy resolves most of the constraint conflicts for you and also
can attach to a constraint conditional closures that are evaluated before applying
a constraint, this way you can install an Auto Layout constraint depending on
platform, size classes, orientation… or the state of your controller, easy peasy!
In this quick tour through EasyPeasy we assume that you already know the
advantages and disadvantages of the different Auto Layout APIs and therefore you
won’t see here a comparison of the code side by side, just read and decide
whether EasyPeasy is for you or not.
A touch of EasyPeasy
The example below is quite simple but shows how effortless its implementation
result using EasyPeasy.
Features
Compatible with iOS, tvOS and OS X.
Lightweight and easy to use domain specific language.
To work with Swift 2.2 use EasyPeasy v.1.2.1 or earlier versions of the library.
To work with Swift 2.3 use EasyPeasy v.1.3.1.
To work with Swift 3 use EasyPeasy v.1.4.2.
To work with Swift 4 use EasyPeasy v.1.8.0.
To work with Swift 5 use EasyPeasy v.1.9.0 and above.
(thanks Bas van Kuijck).
Cocoapods
EasyPeasy is available through CocoaPods. To install
it, simply add the following line to your Podfile:
pod "EasyPeasy"
Carthage
EasyPeasy is Carthage compatible.
To add EasyPeasy as a dependency to your project, just add the following line
to your Cartfile:
github "nakiostudio/EasyPeasy"
And run carthage update as usual.
Compatibility
EasyPeasy is compatible with iOS (8 and above), tvOS (9 and above) and OS X
(10.10 and above).
The framework has been tested with Xcode 7 and Swift 2.0, however don’t hesitate
to report any issues you may find with different versions.
Usage
EasyPeasy is a set of position and dimension attributes that you can apply
to your views. You can manage these from the easy property available within all
the UI classes that work with Auto Layout (view subclasses, layout guides, etc).
For instance, to set a width of 200px to a view you would create
an attribute of class Width with a constant value of 200, then the attribute
is applied to the view by using the easy.layout(_:) method.
myView.easy.layout(Width(200))
Because our view without height is nothing we can apply multiple attributes at
once as follows:
myView.easy.layout(
Width(200),
Height(120)
)
In the previous example, two attributes have been applied and therefore two constraints
created and added: a width constraint with constant = 200 and a height constraint
with constant = 120.
Constants
Without really knowing it, we have just created an EasyPeasyConstant struct
containing the constant, multipler and the relation of a NSLayoutConstraint.
Relations
EasyPeasy provides an easy way of creating constants with different
NSLayoutRelations:
.Equal: it is created like in our previous example Width(200).
.GreaterThanOrEqual: it is created as easy as this Width(>=200) and it means
that our view has a width greater than or equal to 200px.
.LessThanOrEqual: it is created as follows Width(<=200).
Multipliers
There is a custom operator that eases the creation of a NSLayoutConstraint multiplier.
You can use it like this Width(*2) and means that the width of our view is two times
something, we will mention later how to establish the relationship with that something.
In addition, you can combine multipliers with Equal, .GreaterThanOrEqual and
LessThanOrEqual relations. i.e. Width(>=10.0*0.5) creates a NSLayoutConstraint
with value = 10.0, relation = .GreaterThanOrEqual and multiplier = 0.5, whereas
Width(==10.0*0.5) creates a NSLayoutConstraint with value = 10.0,
relation = .Equal and multiplier = 0.5.
Attributes
EasyPeasy provides as many Attribute classes as attributes NSLayoutConstraint
have, plus something that we have called CompoundAttributes (we will explain these
attributes later).
DimensionAttributes
There are just two dimension attributes Width and Height. You can create an
Auto Layout relationship between your view DimensionAttribute and another view
by using the method func like(view: UIView) -> Self. Example:
That line of code will create a constraint that sets a width for contentLabel
equal to the headerView width.
PositionAttributes
The table below shows the different position attributes available. Because they
behave like the NSLayoutConstraint attributes, you can find a complete
description of them in the Apple docs.
Attribute
Attribute
Attribute
Attribute
Left
Right
Top
Bottom
Leading
Trailing
CenterX
CenterY
LeftMargin
RightMargin
TopMargin
BottomMargin
LeadingMargin
TrailingMargin
CenterXWithinMargins
CenterYWithinMargins
FirstBaseline
LastBaseline
–
–
As well as the DimensionAttributes have the like: method to establish
Auto Layout relationships, you can use a similar method to do the same with
PositionAttributes. This method is:
These attributes are the ones that create multiple DimensionAttributes or
PositionAttributes under the hood. For example, the Size attribute will create
a Width and a Height attributes with their width and height
NSLayoutConstraints respectively.
These are the CompoundAttributes available:
Size: As mentioned before this attribute will apply a Width and a Height
attribute to the view. It can be initialized in many ways and depending on that
the result may change. These are some examples:
Edges: This attribute creates Left, Right, Top and Bottom attributes
at once. Examples:
// Apply left = 0, right = 0, top = 0 and bottom = 0 constraints to its superview
view.easy.layout(Edges())
// Apply left = 10, right = 10, top = 10 and bottom = 10 constraints to its superview
view.easy.layout(Edges(10))
// Apply left = 10, right = 10, top = 5 and bottom = 5 constraints to its superview
view.easy.layout(Edges(UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)))
Center: It creates CenterX and CenterY attributes. Examples:
// Apply centerX = 0 and centerY = 0 constraints to its superview
view.easy.layout(Center())
// Apply centerX = 10 and centerY = 10 constraints to its superview
view.easy.layout(Center(10))
// Apply centerX = 0 and centerY = 50 constraints to its superview
view.easy.layout(Center(CGPoint(x: 0, y: 50)))
Margins: This attribute creates LeftMargin, RightMargin, TopMargin and
BottomMargin attributes at once. Examples:
// Apply leftMargin = 0, rightMargin = 0, topMargin = 0 and bottomMargin = 0 constraints to its superview
view.easy.layout(Margins())
// Apply leftMargin = 10, rightMargin = 10, topMargin = 10 and bottomMargin = 10 constraints to its superview
view.easy.layout(Margins(10))
// Apply leftMargin = 10, rightMargin = 10, topMargin = 5 and bottomMargin = 5 constraints to its superview
view.easy.layout(Margins(UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)))
CenterWithinMargins: It creates CenterXWithinMargins and CenterYWithinMargins
attributes. Examples:
// Apply centerXWithinMargins = 0 and centerYWithinMargins = 0 constraints to its superview
view.easy.layout(CenterWithinMargins())
// Apply centerXWithinMargins = 10 and centerYWithinMargins = 10 constraints to its superview
view.easy.layout(CenterWithinMargins(10))
// Apply centerXWithinMargins = 0 and centerYWithinMargins = 50 constraints to its superview
view.easy.layout(CenterWithinMargins(CGPoint(x: 0, y: 50)))
Priorities
The Priority enum does the same function as UILayoutPriority and it’s shaped
by five cases:
low: it creates an Auto Layout priority with Float value 1.
medium: it creates an Auto Layout priority with Float value 500.
high: it creates an Auto Layout priority with Float value 750.
required: it creates an Auto Layout priority with Float value 1000.
custom: it specifies the Auto Layout priority defined by the
developer in the case associated value value. Example: .custom(value: 650.0).
In order to apply any of these priorities to an Attribute, the method
.with(priority: Priority) must be used. The following example gives an
UILayoutPriority of 500 to the TopAttribute applied to view:
view.easy.layout(Top(>=50).with(.medium))
You can also apply a Priority to an array of Attributes (this operation will
override the priorities previously applied to an Attribute).
These Condition closures can be re-evaluated during the lifecycle of a view,
to do so you just need to call the convenience method easy.reload().
view.easy.reload()
Bare in mind that these Condition closures are stored in properties therefore
you need to capture those variables you access within the closure. For example:
This iOS only feature is a variant of the Condition closures that receive no
parameters and return a boolean value. Instead, a Context struct is passed
as parameter providing some extra information based on the UITraitCollection
of the UIView the Attributes are going to be applied to.
The properties available on this Context struct are:
isPad: true if the current device is iPad.
isPhone: true if the current device is iPhone.
isHorizontalVerticalCompact: true if both horizontal and vertical size
classes are .Compact.
isHorizontalCompact: true if the horizontal size class is .Compact.
isVerticalCompact: true if the vertical size class is .Compact.
isHorizontalVerticalRegular: true if both horizontal and vertical size
classes are .Regular.
isHorizontalRegular: true if the horizontal size class is .Regular.
isVerticalRegular: true if the vertical size class is .Regular.
This is an example of ContextualConditions applied to an array of
Attributes:
As we have seen before, you can re-evaluate a Condition closure by calling
the easy.reload() convenience method. This also applies to
ContextualConditions, therefore if you want your constraints to be updated
upon a change on your view UITraitCollection then you need to call the
easy.reload() method within traitCollectionDidChange(_:).
Alternatively, EasyPeasy can do this step for you automatically. This is
disabled by default as it requires method swizzling; to enable it simply
compile the framework adding the compiler flags -D EASY_RELOAD.
UILayoutGuides
Since the version v.0.2.3 (and for iOS 9 projects and above) EasyPeasy
integrates UILayoutGuides support.
Applying constraints
Applying a constraint to an UILayoutGuide is as easy as we have discussed in the
previous sections, just apply the EasyPeasy attributes you want using the
easy.layout(_:) method.
As you can see, all the different attributes and goodies EasyPeasy provides for
UIViews are also applicable to UILayoutGuides.
Connecting UILayoutGuides and UIViews
As mentioned in the Attributes section you can create constraint
relationships between an UIView attribute and other UIViews attributes using
the methods to(_:_) and like(_:_). Now you can take advantage of those methods
to create a relationship between your UIView attributes and an UILayoutGuide.
let layoutGuide = UILayoutGuide()
let separatorView: UIView
let label: UILabel
func setupLabel() {
self.label.easy.layout(
Top(10).to(self.layoutGuide),
CenterX(0),
Size(60)
)
self.separatorView.easy.layout(
Width(0).like(self.layoutGuide),
Height(2),
Top(10).to(self.label),
CenterX(0).to(self.label)
)
}
Lastly
Finally but not less important in this section we will explain how to interact
with Attributes once they have been applied to an UIView using the
easy.layout(_:) method.
Updating constraints
We briefly mentioned in the introductory section that EasyPeasy solves most
of the constraint conflicts and it’s true. Usually, in order to update a constraint
or the constant of a constraint you have to keep a reference to your
NSLayoutConstraint and update the constant when needed. With EasyPeasy you
just need to apply another Attribute to your UIView of the same or different
type. In the example below we have two methods, the one in which we setup our
constraints viewDidLoad() and a method in which we want to update the Top
attribute of our headerView.
That’s it! we have updated our Top constraint without caring about keeping
references or installing/uninstalling new constraints.
However, there is some cases in which EasyPeasy cannot prevent a conflict (at
least for now). This is when multiple constraints cannot be satisfied, i.e. existing
a Left and Right constraints it’s also applied a Width constraint (all of them
with the same priority). But EasyPeasy is smart enough to prevent conflicts,
i.e. when replacing a Left and Right attributes with a CenterX attribute.
Clearing constraints
EasyPeasy provides a method extending UIView that clears all the constraints
installed in an UIView by the framework. This method is func easy.clear().
view.easy.clear()
Animating constraints
Animating constraints with EasyPeasy is very straightforward, just apply one
or more Attributes to your view within an animation block and you are ready to
go, without worrying about constraint conflicts. Example:
EasyPeasy is a Swift framework that lets you create Auto Layout constraints programmatically without headaches and never ending boilerplate code. Besides the basics, EasyPeasy resolves most of the constraint conflicts for you and also can attach to a constraint conditional closures that are evaluated before applying a constraint, this way you can install an Auto Layout constraint depending on platform, size classes, orientation… or the state of your controller, easy peasy!
In this quick tour through EasyPeasy we assume that you already know the advantages and disadvantages of the different Auto Layout APIs and therefore you won’t see here a comparison of the code side by side, just read and decide whether EasyPeasy is for you or not.
A touch of EasyPeasy
The example below is quite simple but shows how effortless its implementation result using EasyPeasy.
Features
UILayoutGuideandNSLayoutGuidesupport.Guides
Table of contents
Installation
Swift compatibility
v.1.2.1or earlier versions of the library.v.1.3.1.v.1.4.2.v.1.8.0.v.1.9.0and above. (thanks Bas van Kuijck).Cocoapods
EasyPeasy is available through CocoaPods. To install it, simply add the following line to your Podfile:
Carthage
EasyPeasy is Carthage compatible. To add EasyPeasy as a dependency to your project, just add the following line to your Cartfile:
And run
carthage updateas usual.Compatibility
EasyPeasy is compatible with iOS (8 and above), tvOS (9 and above) and OS X (10.10 and above). The framework has been tested with Xcode 7 and Swift 2.0, however don’t hesitate to report any issues you may find with different versions.
Usage
EasyPeasy is a set of position and dimension attributes that you can apply to your views. You can manage these from the
easyproperty available within all the UI classes that work with Auto Layout (view subclasses, layout guides, etc).For instance, to set a width of 200px to a view you would create an attribute of class
Widthwith a constant value of200, then the attribute is applied to the view by using theeasy.layout(_:)method.Because our view without height is nothing we can apply multiple attributes at once as follows:
In the previous example, two attributes have been applied and therefore two constraints created and added: a width constraint with
constant = 200and a height constraint withconstant = 120.Constants
Without really knowing it, we have just created an EasyPeasy
Constantstruct containing the constant, multipler and the relation of aNSLayoutConstraint.Relations
EasyPeasy provides an easy way of creating constants with different
NSLayoutRelations:.Equal: it is created like in our previous exampleWidth(200)..GreaterThanOrEqual: it is created as easy as thisWidth(>=200)and it means that our view has a width greater than or equal to 200px..LessThanOrEqual: it is created as followsWidth(<=200).Multipliers
There is a custom operator that eases the creation of a
NSLayoutConstraintmultiplier. You can use it like thisWidth(*2)and means that the width of our view is two times something, we will mention later how to establish the relationship with that something.In addition, you can combine
multiplierswithEqual,.GreaterThanOrEqualandLessThanOrEqualrelations. i.e.Width(>=10.0*0.5)creates aNSLayoutConstraintwithvalue = 10.0,relation = .GreaterThanOrEqualandmultiplier = 0.5, whereasWidth(==10.0*0.5)creates aNSLayoutConstraintwithvalue = 10.0,relation = .Equalandmultiplier = 0.5.Attributes
EasyPeasy provides as many
Attributeclasses as attributesNSLayoutConstrainthave, plus something that we have calledCompoundAttributes(we will explain these attributes later).DimensionAttributes
There are just two dimension attributes
WidthandHeight. You can create an Auto Layout relationship between your viewDimensionAttributeand another view by using the methodfunc like(view: UIView) -> Self. Example:That line of code will create a constraint that sets a width for
contentLabelequal to theheaderViewwidth.PositionAttributes
The table below shows the different position attributes available. Because they behave like the
NSLayoutConstraintattributes, you can find a complete description of them in the Apple docs.As well as the DimensionAttributes have the
like:method to establish Auto Layout relationships, you can use a similar method to do the same with PositionAttributes. This method is:The example below positions
contentLabel10px underheaderViewwith the same left margin asheaderView.CompoundAttributes
These attributes are the ones that create multiple
DimensionAttributesorPositionAttributesunder the hood. For example, theSizeattribute will create aWidthand aHeightattributes with their width and heightNSLayoutConstraintsrespectively.These are the
CompoundAttributesavailable:Size: As mentioned before this attribute will apply aWidthand aHeightattribute to the view. It can be initialized in many ways and depending on that the result may change. These are some examples:Edges: This attribute createsLeft,Right,TopandBottomattributes at once. Examples:Center: It createsCenterXandCenterYattributes. Examples:Margins: This attribute createsLeftMargin,RightMargin,TopMarginandBottomMarginattributes at once. Examples:CenterWithinMargins: It createsCenterXWithinMarginsandCenterYWithinMarginsattributes. Examples:Priorities
The
Priorityenum does the same function asUILayoutPriorityand it’s shaped by five cases:low: it creates an Auto Layout priority withFloatvalue1.medium: it creates an Auto Layout priority withFloatvalue500.high: it creates an Auto Layout priority withFloatvalue750.required: it creates an Auto Layout priority withFloatvalue1000.custom: it specifies the Auto Layout priority defined by the developer in the case associated valuevalue. Example:.custom(value: 650.0).In order to apply any of these priorities to an
Attribute, the method.with(priority: Priority)must be used. The following example gives anUILayoutPriorityof500to theTopAttributeapplied toview:You can also apply a
Priorityto an array ofAttributes(this operation will override the priorities previously applied to anAttribute).Conditions
One of the peculiarities of EasyPeasy is the usage of
Conditionsor closures that evaluate whether a constraint should be applied or not to the view.The method
when(condition: Condition)sets theConditionclosure to anAttribute.There is plenty of use cases, the example below shows how to apply different constraints depending on a custom variable:
Condition re-evaluation
These
Conditionclosures can be re-evaluated during the lifecycle of a view, to do so you just need to call the convenience methodeasy.reload().Bare in mind that these
Conditionclosures are stored in properties therefore you need to capture those variables you access within the closure. For example:You can also apply a
Conditionto an array ofAttributes(this operation will override theConditionspreviously applied to anAttribute).ContextualConditions
This iOS only feature is a variant of the
Conditionclosures that receive no parameters and return a boolean value. Instead, aContextstruct is passed as parameter providing some extra information based on theUITraitCollectionof theUIViewtheAttributesare going to be applied to.The properties available on this
Contextstruct are:isPad: true if the current device is iPad.isPhone: true if the current device is iPhone.isHorizontalVerticalCompact: true if both horizontal and vertical size classes are.Compact.isHorizontalCompact: true if the horizontal size class is.Compact.isVerticalCompact: true if the vertical size class is.Compact.isHorizontalVerticalRegular: true if both horizontal and vertical size classes are.Regular.isHorizontalRegular: true if the horizontal size class is.Regular.isVerticalRegular: true if the vertical size class is.Regular.This is an example of
ContextualConditionsapplied to an array ofAttributes:ContextualCondition re-evaluation
As we have seen before, you can re-evaluate a
Conditionclosure by calling theeasy.reload()convenience method. This also applies toContextualConditions, therefore if you want your constraints to be updated upon a change on your viewUITraitCollectionthen you need to call theeasy.reload()method withintraitCollectionDidChange(_:).Alternatively, EasyPeasy can do this step for you automatically. This is disabled by default as it requires method swizzling; to enable it simply compile the framework adding the compiler flags
-D EASY_RELOAD.UILayoutGuides
Since the version v.0.2.3 (and for iOS 9 projects and above) EasyPeasy integrates
UILayoutGuidessupport.Applying constraints
Applying a constraint to an
UILayoutGuideis as easy as we have discussed in the previous sections, just apply the EasyPeasy attributes you want using theeasy.layout(_:)method.As you can see, all the different attributes and goodies EasyPeasy provides for
UIViewsare also applicable toUILayoutGuides.Connecting UILayoutGuides and UIViews
As mentioned in the Attributes section you can create constraint relationships between an
UIViewattribute and otherUIViewsattributes using the methodsto(_:_)andlike(_:_). Now you can take advantage of those methods to create a relationship between yourUIViewattributes and anUILayoutGuide.Lastly
Finally but not less important in this section we will explain how to interact with
Attributesonce they have been applied to anUIViewusing theeasy.layout(_:)method.Updating constraints
We briefly mentioned in the introductory section that EasyPeasy solves most of the constraint conflicts and it’s true. Usually, in order to update a constraint or the constant of a constraint you have to keep a reference to your
NSLayoutConstraintand update the constant when needed. With EasyPeasy you just need to apply anotherAttributeto yourUIViewof the same or different type. In the example below we have two methods, the one in which we setup our constraintsviewDidLoad()and a method in which we want to update theTopattribute of ourheaderView.That’s it! we have updated our
Topconstraint without caring about keeping references or installing/uninstalling new constraints.However, there is some cases in which EasyPeasy cannot prevent a conflict (at least for now). This is when multiple constraints cannot be satisfied, i.e. existing a
LeftandRightconstraints it’s also applied aWidthconstraint (all of them with the same priority). But EasyPeasy is smart enough to prevent conflicts, i.e. when replacing aLeftandRightattributes with aCenterXattribute.Clearing constraints
EasyPeasy provides a method extending
UIViewthat clears all the constraints installed in anUIViewby the framework. This method isfunc easy.clear().Animating constraints
Animating constraints with EasyPeasy is very straightforward, just apply one or more
Attributesto your view within an animation block and you are ready to go, without worrying about constraint conflicts. Example:Example projects
Don’t forget to clone the repository and run the iOS and OS X example projects to see EasyPeasy in action.
Note: the messages in the demo app aren’t real and the appearance of those Twitter accounts no more than a tribute to some kickass developers :)
EasyPeasy playground
Alternatively, you can play with EasyPeasy cloning the Playground project available here.
Autogenerated documentation
EasyPeasy is a well documented framework and therefore all the documented classes and methods are available in Cocoadocs.
Author
Carlos Vidal - @nakiostudio
License
EasyPeasy is available under the MIT license. See the LICENSE file for more info.