Using Cartography, you can set up your Auto Layout constraints in declarative code and without any stringly typing!
In short, it allows you to replace this:
addConstraint(NSLayoutConstraint( item: button1, attribute: .Right, relatedBy: .Equal, toItem: button2, attribute: .Left, multiplier: 1.0, constant: -12.0 ))
with this
constrain(button1, button2) { button1, button2 in button1.right == button2.left - 12 }
If you end up using Cartography in production, I'd love to hear from you. You can reach me through Twitter or email.
To integrate Cartography into your Xcode project using CocoaPods, specify it in your Podfile:
target '<Your Target Name>' do pod 'Cartography', '~> 3.0' end
Then, run the following command:
$ pod install
Call the constrain* function with your UIView or NSView instances as well
as a closure in which you declare the constraints between the different
attributes of your views:
constrain(view1, view2) { view1, view2 in view1.width == (view1.superview!.width - 50) * 0.5 view2.width == view1.width - 50 view1.height == 40 view2.height == view1.height view1.centerX == view1.superview!.centerX view2.centerX == view1.centerX view1.top >= view1.superview!.top + 20 view2.top == view1.bottom + 20 }
For every view on the left hand side of an equality or inequality operator,
Cartography will automatically set its
translatesAutoresizingMaskIntoConstraints property to false.
If the view is
not controlled by you–for example if it belongs to a Apple-provided
UIViewController class–you should take appropriate care when declaring its
constraints.
You can capture multiple constraints in a group to then replace them with new constraints at a later point.
constrain(view) { view in view.width == 100 view.height == 100 } let group = ConstraintGroup() // Attach `view` to the top left corner of its superview constrain(view, replace: group) { view in view.top == view.superview!.top view.left == view.superview!.left } /* Later */ // Move the view to the bottom right corner of its superview constrain(view, replace: group) { view in view.bottom == view.superview!.bottom view.right == view.superview!.right } UIView.animate(withDuration: 0.5, animations: view.layoutIfNeeded)
For convenience, the constrain functions also returns ConstraintGroup
instances:
let group = constrain(button) { button in button.width == 100 button.height == 400 }
Cartography supports all built-in attributes as of iOS 8 and OS X 10.9, those are:
widthheighttoprightbottomleftleadingtrailingcenterXcenterYbaseline
as well as the iOS specific
firstBaselineleftMarginrightMargintopMarginbottomMarginleadingMargintrailingMargincenterXWithinMarginscenterYWithinMarginsedgesWithinMargins
These can be further refined using the following operators: *, /, + and
-.
Additionally, it supports convenient compound attributes that allow you to assign multiple attributes at once:
constrain(view) { view in view.size == view.superview!.size / 2 view.center == view.superview!.center }
constrain(view) { view in view.edges == inset(view.superview!.edges, 20, 20, 40, 20) }
If you need to align multiple views by a common edge, you can use the align
functions:
constrain(view1, view2, view3) { view1, view2, view3 in align(top: view1, view2, view3) }
Which is equivalent to view1.top == view2.top; view2.top == view3.top. Similar
variants exist for top, right bottom, left, leading, trailing,
centerX, centerY and baseline.
For distributing multiple views, either horizontally or vertically, you can use
the distribute functions:
constrain(view1, view2, view3) { view1, view2, view3 in distribute(by: 10, horizontally: view1, view2, view3) }
Which is equivalent to view1.trailing == view2.leading - 10; view2.trailing == view3.leading - 10.
You can set the priorities of your constraints using the ~ operator:
constrain(view) { view in view.width >= 200 ~ UILayoutPriority(100) view.height >= 200 ~ .required }
Since the ==, >=, <= and ~ emit NSLayoutConstraint instances, you can
capture their results if you need to refer to the layout constraints at a later
time:
var width: NSLayoutConstraint? constrain(view) { view in width = (view.width == 200 ~ 100) }
Note that declaring compound attributes returns multiple constraints at once:
var constraints: [NSLayoutConstraint]? constrain(view) { view in constraints = (view.size == view.superview!.size ~ .defaultLow) }
Read the documentation here. For more information, see the gh-pages branch.
* Since Xcode 11 and swift 5.1 the keyword constrain conflicts with the ones used by the CommonUISDK... so, Calling the function with the module name is necessary to make it work properly
e.g.: Cartography.constrain
If you're using it with Xcode 10.3 or earlier, you can still use it as it is, without the module name alongside the function.
For Swift 3.x: Versions <= 1.1.0
For Swift 4.x: Versions >= 2.0.0
For Swift 5.x: Versions >= 4.0.0
Please, don't hesitate to file an issue if you have questions.
Cartography was built by Robb Böhnke, is maintained by Orta Therox and was inspired by the excellent FLKAutoLayout by Florian Kugler.