⚓️ Declarative, extensible, powerful Auto Layout
❤️ Support my apps https://indiegoodies.com/ ❤️
❤️❤️😇😍🤘❤️❤️
I like to build view in code, so Auto Layout is my definite choice. The syntax has improved over the years, but I always want to do it with minimum effort. More repetitive code makes you tend to do copy paste and produce more bugs.
Read more How to make Auto Layout more convenient in iOS
How new APIs were introduced over the years, so you know to set your deployment target
NSLayoutConstraint
since iOS 6, macOS 10.7isActive
since iOS 8, macOS 10.10NSLayoutAnchor
, UI|NSLayoutGuide
since iOS 9, macOS 10.11All the Auto Layout frameworks you see are just convenient ways to build NSLayoutConstraint
, in fact these are what you normally need
addSubview
so that view is in hierarchytranslatesAutoresizingMaskIntoConstraints = false
isActive = true
to enable constraintsMost of the time, NSLayoutAnchor
is what you need. But if you need more, EasyAnchor can be your choice.
Well, you can use Auto Layout to make Tetris
. Auto Layout plays well with affine transform
too. See code
activate(
lineBlock.anchor.left.bottom
)
// later
activate(
firstSquareBlock.anchor.left.equal.to(lineBlock.anchor.right),
firstSquareBlock.anchor.bottom
)
// later
activate(
secondSquareBlock.anchor.right.bottom
)
This is how to make a piano using apply
and fixed spacing
. See code
activate(
c.anchor.left,
b.anchor.right,
c.anchor.top.bottom,
c.anchor.top.bottom.width.apply(to: [d, e, f, g, a, b]),
c.anchor.fixedSpacingHorizontally(togetherWith: [d, e, f, g, a, b], spacing: 0)
)
activate(
cd.anchor.top,
cd.anchor.size.equal.to(c.anchor.size).multiplier(2/3),
cd.anchor.top.size.apply(to: [de, fg, ga, ab]),
cd.anchor.centerX.equal.to(c.anchor.right),
de.anchor.centerX.equal.to(d.anchor.right),
fg.anchor.centerX.equal.to(f.anchor.right),
ga.anchor.centerX.equal.to(g.anchor.right),
ab.anchor.centerX.equal.to(a.anchor.right)
)
More example can be found in Example
LayoutGuide
Prefer composition over extension, this is how you access anchor
. Support View
, LayoutGuide
, LayoutSupport
let view = UIView()
view.anchor.left.right
let guide = UILayoutGuide()
guide.anchor.width.height
topLayoutGuide.anchor.bottom
bottomLayoutGuide.anchor.top
Use activate
which accepts variadic parameters. This is important, no matter how you create constraints, they don’t have any effect until you activate
it ❗❗❗❗❗
activate(
a.anchor.top.left,
b.anchor.top.right,
c.anchor.bottom.left,
d.anchor.bottom.right
)
Supports all attributes you can think of
anchor.top.left.bottom.right
.leading.trailing
.topMargin.bottomMargin.leftMargin.rightMargin
.centerX.centerY
.centerXWithinMargins.centerXWithinMargins
.lastBaseline.firstBaseline
.width.height
a.anchor.top.equal.to(b.bottom)
a.anchor.width.greaterThanOrEqual.to(b.anchor.height)
a.anchor.width.lessThanOrEqual.to(b.anchor)
This is how to apply constant
, multiplier
, priority
, identifier
a.anchor.top.equal.to(b.anchor.bottom)
.constant(10).multiplier(1.5).priority(999)
.id("verticalSpacingBetweenA-B")
Get references to constraints to modify it later on. In the ref
closure, we get access to all the created constraints
var constraint: NSLayoutConstraint?
activate(
view.anchor.center.constant(10).ref({ constraint = $0.first })
)
Use convenient attributes which combine multiple inner attributes
a.anchor.center // centerX, centerY
a.anchor.size // width, height
a.anchor.edges // top, right, bottom, left
Insets
a.anchor.edges.insets(EdgeInsets(top: 1, left: 2, bottom: 3, right: 4)) // top+1, left+2, bottom+3, right+4
a.anchor.edges.insets(5) // top+5, left+5, bottom-5, right-5
Padding
a.anchor.paddingHorizontally(20) // leading+20, trailing-20
b.anchor.paddingVertically(20) // top+20, bottom-20
Size to another anchor
a.anchor.height.equal.to(b.anchor.width)
c.anchor.size.equal.to(d.anchor)
Size to a constant
a.anchor.height.equal.to(20) // height==20
b.anchor.size.equal.to(20) // width==20, height==20
You can’t just use constant
because EasyAnchor will infer to the superview
c.anchor.width.constant(20) // width==superview.width+20
a.anchor.height.equal.to(a.anchor.width) // height==width
Alternatively, you can just use ratio
a.anchor.width.constant(10)
a.anchor.height.ratio(2) // height==width*2
a.anchor.height.constant(10)
a.anchor.width.ratio(2) // width==height*2
You know what you mostly want to do. So does EasyAnchor 🎉. It does its best to infer so don’t have to write “obvious” code
superview
a.anchor.top.left // a.top == a.superview.top, a.left == a.superview.left
a.anchor.top.bottom.width.equal.to(b.anchor) // a.top == b.top, a.bottom == b.bottom, a.width == b.width
You don’t need to declare variables to store constraints
, you can just retrieve them back
a.anchor.find(.height)?.constant = 100
// later
b.anchor.find(.height)?.constant = 100
// later
c.anchor.find(.height)?.constant = 100
Animation is simple. You just change your constraint
's isActive
or constant
properties, then layoutIfNeeded
in an animation block. You can use UIView.animate
or UIViewPropertyAnimator
// Change constraint
a.anchor.find(.height)?.constant = 100
loginButtonHeightConstraint.isActive = false
let animator = UIViewPropertyAnimator(duration: 1, dampingRatio: 0.7)
animator.addAnimations { [weak self] in
self?.view.layoutIfNeeded()
}
animator.startAnimation(afterDelay: 1)
activate
is just a convenient way to produce group
, then set isActive
on the Group
. If you just want to group a set of constraints, then set isActive
later on, use function group
In this example, we have 4 groups, then take turn to toggle which group gets activated
func toggle(_ group: Group) {
[g1, g2, g3, g4].forEach { g in
guard let g = g else {
return
}
g.isActive = (g == group)
}
}
g1 = group(a.anchor.top.left)
g2 = group(a.anchor.top.right)
g3 = group(a.anchor.bottom.right)
g4 = group(a.anchor.bottom.left)
g1.isActive = true
animator = Animator(view: self, animations: [
{
self.toggle(self.g2)
},
{
self.toggle(self.g3)
},
{
self.toggle(self.g4)
},
{
self.toggle(self.g1)
}
])
animator.start()
Group
is a set of NSLayoutConstraint
, which are produced by ConstraintProducer
public protocol ConstraintProducer {
func constraints() -> [NSLayoutConstraint]
}
For now, there is Anchor
and Builder
which conforms to ConstraintProducer
, you can extend EasyAnchor easily by conform to ConstraintProducer
. For example
// This accepts a list of views, and build constraints
class MyAwesomeLayout: ConstraintProducer {
init(views: [UIView]) {
// Your code goes here
}
func constraints() -> [NSLayoutConstraint] {
// Your code goes here
return []
}
}
let awesomeLayout = MyAwesomeLayout(views: [view1, view2])
activate(awesomeLayout)
Well, Anchor
is for making constraints
between 2 views. If you want to make constraints
for multiple views at once, you can use multiple Anchor
. There are some tasks that you do often, let Builder
help you. These method below use Builder
under the hood
EasyAnchor has a set of builders to help you avoid repetitive tasks and build UIs quickly 😎
Apply the same anchor to other views
a.anchor.left.height.apply(to: [b, c]),
Build a paging scrollView horizontally
addSubview(scrollView)
[a, b, c, d].forEach {
scrollView.addSubview($0)
}
activate(
scrollView.anchor.edges.insets(8),
a.anchor.pagingHorizontally(togetherWith: [b, c, d], in: scrollView)
)
Add fixed spacing. The views will resize
activate(
container.anchor.edges.insets(8),
a.anchor.left.top.bottom,
c.anchor.right,
a.anchor.top.bottom.width.apply(to: [b, c]),
a.anchor.fixedSpacingHorizontally(togetherWith: [b, c], spacing: 50)
)
Add dynamic spacing using LayoutGuide. The spacing will resize
activate(
container.anchor.edges.insets(8),
a.anchor.size.equal.to(30),
b.anchor.size.equal.to(30),
c.anchor.size.equal.to(30),
a.anchor.left.centerY,
a.anchor.centerY.apply(to: [b, c]),
c.anchor.right,
a.anchor.dynamicSpacingHorizontally(togetherWith: [b, c])
)
Group
to declare many sets of constraints for different screen sizes / size classesEasyAnchor is available through CocoaPods. To install
it, simply add the following line to your Podfile:
pod 'EasyAnchor'
EasyAnchor is also available through Carthage.
To install just write into your Cartfile:
github "onmyway133/EasyAnchor"
EasyAnchor can also be installed manually. Just download and drop Sources
folders in your project.
Khoa Pham, [email protected]
We would love you to contribute to EasyAnchor, check the CONTRIBUTING file for more info.
EasyAnchor is available under the MIT license. See the LICENSE file for more info.