Auto Layout (and manual layout) in one line

Bamboo

Auto Layout (and manual layout) in one line.

Quick Look

view.bb.centerX().below(view2).size(100)

It’s equivalent to iOS 9 API:

view.centerXAnchor.constraint(equalTo: view.superview!.centerXAnchor).isActive = true
view.topAnchor.constraint(equalTo: view2.bottomAnchor).isActive = true
view.widthAnchor.constraint(equalToConstant: 100).isActive = true
view.heightAnchor.constraint(equalToConstant: 100).isActive = true

As you can see, Bamboo eliminated a lot of redundant code.

  • Using chaining style, you can write view’s name just once.
  • All methods are grouped in bb extension, so their names can be simple and short.
  • Superview and anchor are usually implied.
  • More higher level methods like below() and size() make things easier.

Installation

Carthage

github "wordlessj/Bamboo" ~> 1.0

CocoaPods

pod 'Bamboo', '~> 1.0'

Usage

Basics

All basic anchors on UIView and UILayoutGuide are provided with suffix Anchor stripped, e.g., left instead of leftAnchor.

// view.left == superview.left
view.bb.left()

// Or use it on UILayoutGuide.
layoutGuide.bb.left()

To specify another view’s corresponding anchor, simply pass the view to the method.

// view1.left == view2.left
view1.bb.left(view2)

Of course, you can use a specific anchor.

// view1.left == view2.right
view1.bb.left(view2.rightAnchor)

Including a constant is straightforward using operator + or -.

// view1.left == view2.right + 10
view1.bb.left(view2.rightAnchor + 10)

// Specific view or anchor can be omitted.
// view.left == superview.left + 10
view.bb.left(10)

For dimension anchors, when only a constant is specified, it sets the dimension to the constant, instead of matching to superview.

// view.width == 10
view.bb.width(10)

width() and width(0) are different, the former match to superview and the latter set to 0.

In iOS 11, you can use SystemSpacing in place of constant, pass multiplier to the initializer, only + operator is supported.

// view1.left == view2.right + 2 * SystemSpacing
view1.bb.left(view2.rightAnchor + SystemSpacing(2))

Similarly, use >= or <= to specify equality.

// view1.left >= view2.right + 10
view1.bb.left(>=view2.rightAnchor + 10)

To specify priority, use operator ~.

// view1.left == view2.right + 10 (priority 500)
view1.bb.left(view2.rightAnchor + 10 ~ 500)

For dimension anchors, multiplier can be specified with * or /.

// view1.width == 2 * view2.width
view1.bb.width(2 * view2)

Expression

Expressions are passed to basic anchor methods. Full form:

( >= or <= ) item + constant ~ priority

For dimension anchors:

( >= or <= ) item * multiplier + constant ~ priority

item can be UIView, UILayoutGuide or NSLayoutAnchor. You can use / and - instead of * and +. If equality is not specified, multiplier can be put before item to be more like linear functions. More complex expressions can be created, but for simplicity, it’s not recommended.

Constraints

You can get a single constraint created from the method for later use.

let c: NSLayoutConstraint = view.bb.left().constraint

Or get all constraints accumulated from the chain.

let c: [NSLayoutConstraint] = view.bb.left().top().constraints

Activate and Deactivate

Constraints can be activated and deactivated easily so you can better control the layout change.

// Deactivate after creation.
let c: [NSLayoutConstraint] = view.bb.left().top().constraints.deactivate()

// Activate later when appropriate.
c.activate()

Higher Level Methods

Aspect Ratio

// view.width == 2 * view.height
view.bb.aspectRatio(2)

Center and Size

Both methods are similar to basic anchor methods.

  • center(), consists of centerX and centerY.
  • size(), consists of width and height.

There are two more methods to set size.

let cgSize: CGSize
view.bb.size(cgSize)

view.bb.size(width: 10, height: 20)

Fill

// Pin all edges.
fill()

// Pin corresponding edge and adjacent edges.
// e.g., fillLeft() pin left, top and bottom.
fill[Left|Right|Top|Bottom|Leading|Trailing]()

// Pin edges on corresponding axis.
// e.g., fillWidth() pin leading and trailing.
fill[Width|Height]()

All fill methods take two optional arguments, first is either UIView or UILayoutGuide to be filled, nil for its superview, second is a UIEdgeInsets.

// view1 fills view2 with insets.
let insets: UIEdgeInsets
view1.bb.fill(view2, insets: insets)

// If all edge insets are the same, you can pass a single value.
// view1 fills view2 with insets 10.
view1.bb.fill(view2, insets: 10)

Around

  • before()
  • after()
  • above()
  • below()

These methods take two optional arguments like fill methods, except the second one is spacing.

// view1.trailing == view2.leading - 10
view1.bb.before(view2, spacing: 10)

Offset

In iOS 10, use - operator to create an offset anchor between two axis anchors, then use offset() to create a constraint. It does not matter which view is used before bb.

// view2.left - view1.right == view3.left - view2.right
view1.bb.offset(view2.leftAnchor - view1.rightAnchor, view3.leftAnchor - view2.rightAnchor)

Multiple Items

You can constrain on multiple items at once. There are two fundamental methods on which other methods are built.

// Constrain on each item.
// e.g., Set each item's width to 10.
[view1, view2, view3].bb.each {
    $0.bb.width(10)
}

// Constrain between every two items.
// e.g., view1.left == view2.left, view2.left == view3.left
[view1, view2, view3].bb.between {
    $0.bb.left($1)
}

Most basic methods on single item are available on multiple items.

// Anchor methods take no arguments and align all items' corresponding anchor.
// e.g., Align all views' left.
[view1, view2, view3].bb.left()

// For dimension anchors, you can set values for all items.
// e.g., Set all views' width to 10.
[view1, view2, view3].bb.width(10)

You can distribute items along an axis with fixed spacing or equal spacing.

  • distributeX()
  • distributeY()
  • distributeXEqualSpacing()
  • distributeYEqualSpacing()
// [view1]-10-[view2]-10-[view3]
[view1, view2, view3].bb.distributeX(spacing: 10)

All distribute methods come with an optional inset parameter, you can specify no insets, fixed insets or equal insets.

// |-[view1]-[view2]-[view3]-|, spacings are all equal.
[view1, view2, view3].bb.distributeXEqualSpacing(inset: .equal)

GitHub