EasyAnchor
 
- Push Hero - pure Swift native macOS application to test push notifications
 - PastePal - Pasteboard, note and shortcut manager
 - Quick Check - smart todo manager
 - Alias - App and file shortcut manager
 - My other apps
 
 
Table of contents
- Story
 - Features
 - Basic with Anchor
 - Inference
 - Find existing constraints
 - Animation
 - Update constraints with Group
 - Extensible with ConstraintProducer
 - Build quickly with Builder
 - Debug Auto Layout
 - Support multiple screen sizes
 
Story
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
Auto Layout APIs history
How new APIs were introduced over the years, so you know to set your deployment target
NSLayoutConstraintsince iOS 6, macOS 10.7isActivesince iOS 8, macOS 10.10NSLayoutAnchor,UI|NSLayoutGuidesince iOS 9, macOS 10.11
Do you need another Auto Layout framework?
All the Auto Layout frameworks you see are just convenient ways to build NSLayoutConstraint, in fact these are what you normally need
- Call 
addSubviewso that view is in hierarchy - Set 
translatesAutoresizingMaskIntoConstraints = false - Set 
isActive = trueto enable constraints 
Most of the time, NSLayoutAnchor is what you need. But if you need more, EasyAnchor can be your choice.
Examples
Tetris
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
) 
Piano
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
More example can be found in Example
Features
- Fluent builder syntax
 - Easy to customize with protocol based
 - Support iOS, macOS
 -  Support 
LayoutGuide - Update and reset constraints
 - Find existing constraints
 - Debug constraints
 - Visualize constraints
 
Basic with Anchor
Access Anchor
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 
Activate constraints
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
) 
Attributes
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 
Relation
a.anchor.top.equal.to(b.bottom)
a.anchor.width.greaterThanOrEqual.to(b.anchor.height)
a.anchor.width.lessThanOrEqual.to(b.anchor) 
Configuration
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") 
Reference
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 })
) 
Convenient attributes
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 
Convenient methods
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
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 
Ratio
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 
Inference
You know what you mostly want to do. So does EasyAnchor 
Most of the time, you want to constraint to the superview
 
a.anchor.top.left // a.top == a.superview.top, a.left == a.superview.left 
Most of the time, you want to constraint to the same attributes
a.anchor.top.bottom.width.equal.to(b.anchor) // a.top == b.top, a.bottom == b.bottom, a.width == b.width 
Find existing constraints
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
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) 
Update constraints with Group
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() 
Extensible with ConstraintProducer
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) 
Build quickly with Builder
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
Apply the same anchor to other views
a.anchor.left.height.apply(to: [b, c]), 
Paging
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)
) 
Fixed spacing
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)
) 
Dynamic spacing
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])
) 
Debug Auto Layout
Support multiple screen sizes
- Use 
Groupto declare many sets of constraints for different screen sizes / size classes - Use ratio, read Auto Layout with different screen sizes
 
Installation
EasyAnchor 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.
Author
Khoa Pham, [email protected]
Contributing
We would love you to contribute to EasyAnchor, check the CONTRIBUTING file for more info.
License
EasyAnchor is available under the MIT license. See the LICENSE file for more info.









