Last active
April 11, 2016 07:28
-
-
Save felginep/992e110e12e2a1adca486e1b071dd41e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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