Skip to content

Instantly share code, notes, and snippets.

@chrisbrandow
Last active January 26, 2017 23:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisbrandow/a13342b0ca487b2613eaa06a8ba4c4d8 to your computer and use it in GitHub Desktop.
Save chrisbrandow/a13342b0ca487b2613eaa06a8ba4c4d8 to your computer and use it in GitHub Desktop.
A set of operators to make Autolayout extremely readable
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
///create a constraint of view to its superView
infix operator ➡️|: AdditionPrecedence
infix operator |⬅️: AdditionPrecedence
infix operator -⬆️: AdditionPrecedence
infix operator ⬇️-: AdditionPrecedence
///create a constraint between the designated edge of view to the designated edge of another view
infix operator ➡️=⬅️: TernaryPrecedence
infix operator --: TernaryPrecedence //same as ➡️=⬅️. it's just clearer
infix operator ⬇️=⬆️: TernaryPrecedence
infix operator ⬆️=⬆️: TernaryPrecedence
infix operator ⬇️=⬇️: TernaryPrecedence
infix operator ➡️=➡️: TernaryPrecedence
infix operator ⬅️=⬅️: TernaryPrecedence
infix operator ↔️=↔️: TernaryPrecedence
infix operator ↕️=↕️: TernaryPrecedence
///create a constraint between horizontal centers of two views
infix operator -=-: TernaryPrecedence
///create a constraint between vertical centers of two views
infix operator |=|: TernaryPrecedence
///create a width constraint on a view
infix operator ↔️=: AdditionPrecedence
///create a height constraint on a view
infix operator ↕️=: AdditionPrecedence
///constrain every edge a view to another view
infix operator ⏹=⏹: AdditionPrecedence
extension UIView {
///add a constraint of view to its superView
func 📌<T: Any>(_ oneOrMany: T) {
if let one = oneOrMany as? NSLayoutConstraint {
addConstraint(one)
} else if let many = oneOrMany as? [NSLayoutConstraint] {
addConstraints(many)
} else {
print(oneOrMany.self)
}
}
}
infix operator +: AdditionPrecedence
func +(left: UIView, right: CGFloat) -> ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint) {
let returnBlock = {(view, attribute1, attribute2) -> NSLayoutConstraint in
return NSLayoutConstraint(item: view, attribute: attribute1, relatedBy: NSLayoutRelation.equal, toItem: left, attribute: attribute2, multiplier: 1.0, constant: -right)
}
return returnBlock
}
func +(left: UIView, right: (CGFloat, CGFloat)) -> ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint) {
let returnBlock: ((UIView, NSLayoutAttribute, NSLayoutAttribute) -> NSLayoutConstraint) = {(view, attribute1, attribute2) -> NSLayoutConstraint in
let constraint = NSLayoutConstraint(item: view, attribute: attribute1, relatedBy: NSLayoutRelation.equal, toItem: left, attribute: attribute2, multiplier: 1.0, constant: -right.0)
constraint.priority = UILayoutPriority(right.1)
return constraint
}
return returnBlock
}
infix operator ++: TernaryPrecedence
func ++(left: NSLayoutConstraint, right: NSLayoutConstraint) -> [NSLayoutConstraint] {
return [left, right]
}
func ++(left: NSLayoutConstraint, right: [NSLayoutConstraint]) -> [NSLayoutConstraint] {
return [left] + right
}
func ++(left: [NSLayoutConstraint], right: [NSLayoutConstraint]) -> [NSLayoutConstraint] {
return left + right
}
func ++(left: [NSLayoutConstraint], right: NSLayoutConstraint) -> [NSLayoutConstraint] {
return left + [right]
}
func ++<T: Any>(_ left: T, right: T) -> [NSLayoutConstraint] {
if let l = left as? NSLayoutConstraint {
if let r = right as? NSLayoutConstraint {
return [l, r]
} else if let r = right as? [NSLayoutConstraint] {
return [l] + r
}
} else if let l = left as? [NSLayoutConstraint] {
if let r = right as? NSLayoutConstraint {
return l + [r]
} else if let r = right as? [NSLayoutConstraint] {
return l + r
}
}
fatalError()
}
infix operator ‼️: MultiplicationPrecedence
func ‼️(left: CGFloat, right: Double) -> (CGFloat, CGFloat) {
return (left, CGFloat(right))
}
infix operator -: AdditionPrecedence
func -(left: UIView, right: CGFloat) -> ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint) {
let returnBlock = {(view, attribute1, attribute2) -> NSLayoutConstraint in
return NSLayoutConstraint(item: view, attribute: attribute1, relatedBy: NSLayoutRelation.equal, toItem: left, attribute: attribute2, multiplier: 1.0, constant: right)
}
return returnBlock
}
func -(left: UIView, right: (CGFloat, CGFloat)) -> ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint) {
let returnBlock: ((UIView, NSLayoutAttribute, NSLayoutAttribute) -> NSLayoutConstraint) = {(view, attribute1, attribute2) -> NSLayoutConstraint in
let constraint = NSLayoutConstraint(item: view, attribute: attribute1, relatedBy: NSLayoutRelation.equal, toItem: left, attribute: attribute2, multiplier: 1.0, constant: right.0)
constraint.priority = UILayoutPriority(right.1)
return constraint
}
return returnBlock
}
func ➡️| (left: UIView, right: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint.constraints(withVisualFormat: "H:[left]-\(right)-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["left": left]).first!
}
func |⬅️ (left: UIView, right: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint.constraints(withVisualFormat: "H:|-\(right)-[left]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["left": left]).first!
}
func -⬆️ (left: UIView, right: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(right)-[left]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["left": left]).first!
}
func ⬇️- (left: UIView, right: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint.constraints(withVisualFormat: "V:[left]-\(right)-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["left": left]).first!
}
func ➡️=⬅️ (left: UIView, right: UIView) -> NSLayoutConstraint {
return left ➡️=⬅️ right + 0
}
func ➡️=⬅️ (left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .trailing, .leading)
}
func --(left: UIView, right: UIView) -> NSLayoutConstraint {//it's just too obvious not to include it
return left ➡️=⬅️ right + 0
}
func --(left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .trailing, .leading)
}
func ⬇️=⬆️ (left: UIView, right: UIView) -> NSLayoutConstraint {
return left ⬇️=⬆️ right + 0
}
func ⬇️=⬆️ (left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .bottom, .top)
}
func ⬆️=⬆️ (left: UIView, right: UIView) -> NSLayoutConstraint {
return left ⬆️=⬆️ right + 0
}
func ⬆️=⬆️ (left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .top, .top)
}
func ⬇️=⬇️ (left: UIView, right: UIView) -> NSLayoutConstraint {
return left ⬇️=⬇️ (right + 0)
}
func ⬇️=⬇️ (left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .bottom, .bottom)
}
func ➡️=➡️ (left: UIView, right: UIView) -> NSLayoutConstraint {
return left ➡️=➡️ (right + 0)
}
func ➡️=➡️ (left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .right, .right)
}
func ⬅️=⬅️ (left: UIView, right: UIView) -> NSLayoutConstraint {
return left ⬅️=⬅️ (right + 0)
}
func ⬅️=⬅️ (left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .left, .left)
}
func ↔️=↔️ (left: UIView, right: UIView) -> NSLayoutConstraint {
return left ↔️=↔️ right + 0
}
func ↔️=↔️(left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .width, .width)
}
func ↕️=↕️(left: UIView, right: UIView) -> NSLayoutConstraint {
return left ↕️=↕️ right + 0
}
func ↕️=↕️ (left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .height, .height)
}
func -=-(left: UIView, right: UIView) -> NSLayoutConstraint {
return left -=- right + 0
}
func -=- (left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .centerY, .centerY)
}
func |=|(left: UIView, right: UIView) -> NSLayoutConstraint {
return left |=| right + 0
}
func |=| (left: UIView, right: ((UIView, NSLayoutAttribute, NSLayoutAttribute) ->NSLayoutConstraint)) -> NSLayoutConstraint {
return right(left, .centerX, .centerX)
}
func ↔️= (view: UIView, width: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint.constraints(withVisualFormat: "H:[view(==\(width))]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view": view]).first!
}
func ↕️= (view: UIView, height: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint.constraints(withVisualFormat: "V:[view(==\(height))]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view": view]).first!
}
func ⏹=⏹ (left: UIView, right: UIView) -> [NSLayoutConstraint] {
return [
left ⬆️=⬆️ right,
left ⬇️=⬇️ right,
left ⬅️=⬅️ right,
left ➡️=➡️ right,
]
}
//transforms ↩️↪️
//animations?🔂
let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 375.0, height: 300))
PlaygroundPage.current.liveView = containerView
containerView.backgroundColor = .brown
let redButton = UIButton()
redButton.backgroundColor = .red
redButton.setTitleColor(.blue, for: .normal)
redButton.setTitleColor(.blue, for: .selected)
redButton.translatesAutoresizingMaskIntoConstraints = false
let blueButton = UIButton()
blueButton.translatesAutoresizingMaskIntoConstraints = false
blueButton.backgroundColor = .blue
let orangeButton = UIButton()
orangeButton.translatesAutoresizingMaskIntoConstraints = false
orangeButton.backgroundColor = .orange
let grayView = UIView()
grayView.translatesAutoresizingMaskIntoConstraints = false
grayView.backgroundColor = .lightGray
containerView.addSubview(grayView)
containerView.addSubview(redButton)
containerView.addSubview(blueButton)
containerView.addSubview(orangeButton)
containerView.📌(grayView |⬅️ 20.0)
containerView.📌(grayView -⬆️ 20.0)
containerView.📌(grayView ⬇️- 20.0)
containerView.📌(grayView ➡️| 20.0)
containerView.📌(redButton |⬅️ 60 ++ redButton -⬆️ 60 ++ redButton ↕️= 40 ++ redButton ↔️= 40)
containerView.📌(redButton ⬇️=⬆️ blueButton + 100 ‼️ 800.0)
containerView.📌(redButton ⬇️=⬆️ blueButton + 10 ‼️ 900.0) //overrides previous due to precedence
containerView.📌(redButton |=| blueButton) //align vertical centers
containerView.📌(redButton ↔️=↔️ blueButton + 20)
containerView.📌(blueButton ↕️= 100)
containerView.📌((blueButton--orangeButton + 40) ++ (blueButton-=-orangeButton)) //precedence requires parentheses around constraints with other operators
orangeButton.📌(orangeButton ↕️= 45 ++ orangeButton ↔️= 25)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment