Skip to content

Instantly share code, notes, and snippets.

@felginep
Last active April 11, 2016 07:28
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 felginep/992e110e12e2a1adca486e1b071dd41e to your computer and use it in GitHub Desktop.
Save felginep/992e110e12e2a1adca486e1b071dd41e to your computer and use it in GitHub Desktop.
struct ConstraintBuilder {
var firstItem: UIView?
var firstAttribute: NSLayoutAttribute?
var relation: NSLayoutRelation?
var secondAttribute: NSLayoutAttribute?
var secondItem: UIView?
var multiplier: CGFloat = 1.0
var constant: CGFloat = 0.0
}
extension ConstraintBuilder {
init(firstItem: UIView, firstAttribute: NSLayoutAttribute) {
self.firstItem = firstItem
self.firstAttribute = firstAttribute
}
}
extension UIView {
var left: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .Left)
}
var right: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .Right)
}
var top: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .Top)
}
var bottom: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .Bottom)
}
var leading: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .Leading)
}
var trailing: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .Trailing)
}
var width: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .Width)
}
var height: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .Height)
}
var centerX: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .CenterX)
}
var centerY: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .CenterY)
}
var baseline: ConstraintBuilder {
return ConstraintBuilder(firstItem: self, firstAttribute: .Baseline)
}
var centers: [ConstraintBuilder] {
return [centerX, centerY]
}
var edges: [ConstraintBuilder] {
return [left, right, top, bottom]
}
}
extension ConstraintBuilder {
func equals(constraintBuilder: ConstraintBuilder) -> NSLayoutConstraint {
return constraintWithBuilder(constraintBuilder, relation: .Equal)
}
func lessThanOrEquals(constraintBuilder: ConstraintBuilder) -> NSLayoutConstraint {
return constraintWithBuilder(constraintBuilder, relation: .LessThanOrEqual)
}
func greaterThanOrEquals(constraintBuilder: ConstraintBuilder) -> NSLayoutConstraint {
return constraintWithBuilder(constraintBuilder, relation: .GreaterThanOrEqual)
}
func equals(value: CGFloat) -> NSLayoutConstraint {
return constraintWithValue(value, relation: .Equal)
}
func lessThanOrEquals(value: CGFloat) -> NSLayoutConstraint {
return constraintWithValue(value, relation: .LessThanOrEqual)
}
func greaterThanOrEquals(value: CGFloat) -> NSLayoutConstraint {
return constraintWithValue(value, relation: .GreaterThanOrEqual)
}
private func constraintWithBuilder(constraintBuilder: ConstraintBuilder, relation: NSLayoutRelation) -> NSLayoutConstraint {
var selfCopy = self
selfCopy.relation = relation
selfCopy.secondItem = constraintBuilder.firstItem
selfCopy.secondAttribute = constraintBuilder.firstAttribute
return selfCopy.build()
}
private func constraintWithValue(value: CGFloat, relation: NSLayoutRelation) -> NSLayoutConstraint {
var selfCopy = self
selfCopy.relation = relation
selfCopy.constant = value
return selfCopy.build()
}
}
typealias ConstraintBuilderType = (ConstraintBuilder)
extension SequenceType where Generator.Element == ConstraintBuilderType {
func equals(constraintBuilders: [ConstraintBuilder]) -> [NSLayoutConstraint] {
var constraints: [NSLayoutConstraint] = []
for constraint in self {
let otherConstraint = constraintBuilders.filter {
constraint.firstAttribute != nil && $0.firstAttribute == constraint.firstAttribute
}.first
if let otherConstraint = otherConstraint {
constraints.append(constraint.equals(otherConstraint))
}
}
return constraints
}
}
extension ConstraintBuilder {
func build() -> NSLayoutConstraint {
return NSLayoutConstraint(
item: firstItem!,
attribute: firstAttribute!,
relatedBy: relation!,
toItem: secondItem,
attribute: secondAttribute ?? .NotAnAttribute,
multiplier: multiplier,
constant: constant
)
}
}
extension NSLayoutConstraint {
func plus(value: CGFloat) -> NSLayoutConstraint {
constant += value
return self
}
func minus(value: CGFloat) -> NSLayoutConstraint {
return plus(-value)
}
func times(value: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint(
item: firstItem,
attribute: firstAttribute,
relatedBy: relation,
toItem: secondItem,
attribute: secondAttribute,
multiplier: value,
constant: constant
)
}
}
extension NSLayoutConstraint {
func install() {
guard let firstItem = firstItem as? UIView else {
fatalError("First item is not an UIView")
}
let ancestor = commonAncestor(firstItem, view2: secondItem as? UIView)
ancestor?.addConstraint(self)
}
private func commonAncestor(view1: UIView, view2: UIView?) -> UIView? {
guard let view2 = view2 else {
return view1.superview
}
return view1.nearestCommonSuperviewWith(view2)
}
}
typealias NSLayoutConstraintType = (NSLayoutConstraint)
extension SequenceType where Generator.Element == NSLayoutConstraintType {
func install() {
self.map { $0.install() }
}
}
extension UIView {
func nearestCommonSuperviewWith(otherView: UIView) -> UIView? {
return self.viewHierarchy().intersect(otherView.viewHierarchy()).first
}
private func viewHierarchy() -> Set<UIView> {
return Set(UIView.hierarchyFor(self, accumulator: []))
}
static private func hierarchyFor(view: UIView?, accumulator: [UIView]) -> [UIView] {
guard let view = view else {
return accumulator
}
return UIView.hierarchyFor(view.superview, accumulator: accumulator + [view])
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment