Last active
January 4, 2016 19:53
-
-
Save mayoff/c17bccb3d455b724ec40 to your computer and use it in GitHub Desktop.
This file contains 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
#if os(iOS) | |
import UIKit | |
typealias NSLayoutPriority = UILayoutPriority | |
#elseif os(OSX) | |
import AppKit | |
#endif | |
public extension NSLayoutAttribute { | |
/** Return a LayoutAnchor representing attribute `self` of `item`. */ | |
public func of(item: AnyObject) -> LayoutAnchor { | |
return LayoutAnchor(item: item, attribute: self) | |
} | |
} | |
/** | |
I represent an attribute of an item that can participate in auto layout. For example, I can be the X-center of a button, or the baseline of a label, or the trailing edge of an image view. | |
You can use me to create constraints, like this: | |
typealias Layout = NSLayoutAttribute | |
Layout.Top.of(button) == Layout.Top.of(button.superview) + 20 | |
The value of that expression is an `NSLayoutConstraint`. You might want to active it, change its priority, and save it in a property. | |
You can change a constraint's priority using `%=`, like this: | |
Layout.Top.of(button) == Layout.Top.of(button.superview) + 20 %= NSLayoutPriorityDefaultLow | |
Here's a fuller example: | |
NSLayoutConstraint.activateConstraints([ | |
Layout.Top.of(button) == Layout.Top.of(button.superview) + 20 %= NSLayoutPriorityDefaultLow, | |
Layout.Bottom.of(superview) == Layout.Bottom.of(button) + 20, | |
Layout.Left.of(button) == Layout.Left.of(button.superview) + 20, | |
Layout.Right.of(button) <= Layout.Left.of(label) - 10 | |
Layout.Right.of(label) == Layout.Right.of(label.superview) - 20, | |
Layout.Baseline.of(button) == Layout.Baseline.of(label), | |
]) | |
*/ | |
public struct LayoutAnchor { | |
let item: AnyObject? | |
let attribute: NSLayoutAttribute | |
let factor: CGFloat | |
let constant: CGFloat | |
private init(item: AnyObject?, attribute: NSLayoutAttribute, factor: CGFloat, constant: CGFloat) { | |
self.item = item | |
self.attribute = attribute | |
self.factor = factor | |
self.constant = constant | |
} | |
} | |
func *(anchor: LayoutAnchor, factor: CGFloat) -> LayoutAnchor { | |
assert(factor != 0, "\(anchor) can't be multiplied by zero.") | |
return LayoutAnchor(item: anchor.item, attribute: anchor.attribute, factor: factor * anchor.factor, constant: factor * anchor.constant) | |
} | |
func *(factor: CGFloat, anchor: LayoutAnchor) -> LayoutAnchor { | |
return anchor * factor | |
} | |
func /(anchor: LayoutAnchor, factor: CGFloat) -> LayoutAnchor { | |
return LayoutAnchor(item: anchor.item, attribute: anchor.attribute, factor: anchor.factor / factor, constant: anchor.constant / factor) | |
} | |
func +(anchor: LayoutAnchor, constant: CGFloat) -> LayoutAnchor { | |
return LayoutAnchor(item: anchor.item, attribute: anchor.attribute, factor: anchor.factor, constant: anchor.constant + constant) | |
} | |
func +(constant: CGFloat, anchor: LayoutAnchor) -> LayoutAnchor { | |
return LayoutAnchor(item: anchor.item, attribute: anchor.attribute, factor: anchor.factor, constant: anchor.constant + constant) | |
} | |
func -(anchor: LayoutAnchor, constant: CGFloat) -> LayoutAnchor { | |
return anchor + -constant | |
} | |
func -(constant: CGFloat, anchor: LayoutAnchor) -> LayoutAnchor { | |
return constant + -1 * anchor | |
} | |
public func ==(lhs: LayoutAnchor, rhs: LayoutAnchor) -> NSLayoutConstraint { | |
return constraintWithRelation(.Equal, from: lhs, to: rhs) | |
} | |
public func ==(lhs: CGFloat, rhs: LayoutAnchor) -> NSLayoutConstraint { | |
return constraintWithRelation(.Equal, from: lhs, to: rhs) | |
} | |
public func ==(lhs: LayoutAnchor, rhs: CGFloat) -> NSLayoutConstraint { | |
return constraintWithRelation(.Equal, from: lhs, to: rhs) | |
} | |
public func <=(lhs: LayoutAnchor, rhs: LayoutAnchor) -> NSLayoutConstraint { | |
return constraintWithRelation(.LessThanOrEqual, from: lhs, to: rhs) | |
} | |
public func <=(lhs: CGFloat, rhs: LayoutAnchor) -> NSLayoutConstraint { | |
return constraintWithRelation(.LessThanOrEqual, from: lhs, to: rhs) | |
} | |
public func <=(lhs: LayoutAnchor, rhs: CGFloat) -> NSLayoutConstraint { | |
return constraintWithRelation(.LessThanOrEqual, from: lhs, to: rhs) | |
} | |
public func >=(lhs: LayoutAnchor, rhs: LayoutAnchor) -> NSLayoutConstraint { | |
return constraintWithRelation(.GreaterThanOrEqual, from: lhs, to: rhs) | |
} | |
public func >=(lhs: CGFloat, rhs: LayoutAnchor) -> NSLayoutConstraint { | |
return constraintWithRelation(.GreaterThanOrEqual, from: lhs, to: rhs) | |
} | |
public func >=(lhs: LayoutAnchor, rhs: CGFloat) -> NSLayoutConstraint { | |
return constraintWithRelation(.GreaterThanOrEqual, from: lhs, to: rhs) | |
} | |
func %=(constraint: NSLayoutConstraint, priority: NSLayoutPriority) -> NSLayoutConstraint { | |
constraint.priority = priority | |
return constraint | |
} | |
func *=(identifier: String, constraint: NSLayoutConstraint) -> NSLayoutConstraint { | |
constraint.identifier = identifier | |
return constraint | |
} | |
//////////////////////////////////////////////////////////////////////// | |
// MARK: - Implementation details | |
private extension LayoutAnchor { | |
private init(item: AnyObject, attribute: NSLayoutAttribute) { | |
self.init(item: item, attribute: attribute, factor: 1, constant: 0) | |
} | |
// Note: never let constant-only LayoutAnchors escape to the user. They must be immediately used to create constraints, which are then returned. If the user gets his hands on a constant-only LayoutAnchor, he can try to create a constraint between two constants, and that will crash. | |
private init(constant: CGFloat) { | |
self.init(item: nil, attribute: .NotAnAttribute, factor: 1, constant: constant) | |
} | |
} | |
private func constraintWithRelation(relation: NSLayoutRelation, from lhs: LayoutAnchor, to rhs: LayoutAnchor) -> NSLayoutConstraint { | |
if lhs.item == nil { | |
assert(rhs.item != nil, "I don't know how you managed it, but you've asked for a constraint between two constants \(lhs) and \(rhs).") | |
return constraintWithRelation(relation.opposite, from: rhs, to: lhs) | |
} | |
let constant = (rhs.constant - lhs.constant) / lhs.factor | |
let factor = rhs.factor / lhs.factor | |
let relation = lhs.factor < 0 ? relation.opposite : relation | |
return NSLayoutConstraint(item: lhs.item!, attribute: lhs.attribute, relatedBy: relation, toItem: rhs.item, attribute: rhs.attribute, multiplier: factor, constant: constant) | |
} | |
private func constraintWithRelation(relation: NSLayoutRelation, from lhs: LayoutAnchor, to rhs: CGFloat) -> NSLayoutConstraint { | |
return constraintWithRelation(relation, from: lhs, to: LayoutAnchor(constant: rhs)) | |
} | |
private func constraintWithRelation(relation: NSLayoutRelation, from lhs: CGFloat, to rhs: LayoutAnchor) -> NSLayoutConstraint { | |
return constraintWithRelation(relation, from: LayoutAnchor(constant: lhs), to: rhs) | |
} | |
private extension NSLayoutRelation { | |
private var opposite: NSLayoutRelation { | |
switch self { | |
case .Equal: return self | |
case .LessThanOrEqual: return .GreaterThanOrEqual | |
case .GreaterThanOrEqual: return .LessThanOrEqual | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment