1
1
mirror of https://github.com/robb/Cartography.git synced 2024-10-06 21:47:18 +03:00

Merge branch 'master' into xcode6-3

This commit is contained in:
Robert Böhnke 2015-03-23 11:28:18 +01:00
commit 4b41a72f48
17 changed files with 532 additions and 148 deletions

View File

@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "Cartography"
s.version = "0.2.1"
s.version = "0.3.0-beta1"
s.summary = "Declarative Auto Layout in Swift"
s.description = <<-DESC

View File

@ -14,42 +14,107 @@ import AppKit
private func makeEqual<P: RelativeEquality>(attribute: LayoutProxy -> P, first: LayoutProxy, rest: [LayoutProxy]) -> [NSLayoutConstraint] {
return reduce(rest, []) { acc, current in
current.view.car_translatesAutoresizingMaskIntoConstraints = false
return acc + [ attribute(first) == attribute(current) ]
}
}
/// Aligns multiple views by their top edge.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func align(top first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return makeEqual({ $0.top }, first, rest)
}
/// Aligns multiple views by their right edge.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func align(right first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return makeEqual({ $0.right }, first, rest)
}
/// Aligns multiple views by their bottom edge.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func align(bottom first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return makeEqual({ $0.bottom }, first, rest)
}
/// Aligns multiple views by their left edge.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func align(left first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return makeEqual({ $0.left }, first, rest)
}
/// Aligns multiple views by their leading edge.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func align(leading first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return makeEqual({ $0.leading }, first, rest)
}
/// Aligns multiple vies by their trailing edge.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func align(trailing first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return makeEqual({ $0.trailing }, first, rest)
}
/// Aligns multiple views by their horizontal center.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func align(centerX first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return makeEqual({ $0.centerX }, first, rest)
}
/// Aligns multiple views by their vertical center.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func align(centerY first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return makeEqual({ $0.centerY }, first, rest)
}
/// Aligns multiple views by their baseline.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func align(baseline first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return makeEqual({ $0.baseline }, first, rest)
}

View File

@ -16,37 +16,25 @@ public class Context {
internal var constraints: [Constraint] = []
internal func addConstraint(from: Property, to: Property? = nil, coefficients: Coefficients = Coefficients(), relation: NSLayoutRelation = .Equal) -> NSLayoutConstraint {
from.view.car_disableTranslatesAutoresizingMaskIntoConstraintsIfPossible()
to?.view.car_disableTranslatesAutoresizingMaskIntoConstraintsIfPossible()
var toAttribute: NSLayoutAttribute! = NSLayoutAttribute.NotAnAttribute
if to == nil {
toAttribute = NSLayoutAttribute.NotAnAttribute
} else {
toAttribute = to!.attribute
}
var targetView: View
if let toView = to?.view {
let target = closestCommonAncestor(from.view, toView)
assert(target != .None, "No common superview found between \(from.view) and \(toView)")
targetView = target!
} else {
targetView = from.view
}
from.view.car_translatesAutoresizingMaskIntoConstraints = false
let layoutConstraint = NSLayoutConstraint(item: from.view,
attribute: from.attribute,
relatedBy: relation,
toItem: to?.view,
attribute: toAttribute,
attribute: to?.attribute ?? .NotAnAttribute,
multiplier: CGFloat(coefficients.multiplier),
constant: CGFloat(coefficients.constant))
constraints += [ Constraint(view: targetView, layoutConstraint: layoutConstraint) ]
let targetView: View = to.map { to in
let commonSuperivew = closestCommonAncestor(from.view, to.view)
assert(commonSuperivew != .None, "No common superview found between \(from.view) and \(to.view)")
return commonSuperivew!
} ?? from.view
constraints.append(Constraint(view: targetView, layoutConstraint: layoutConstraint))
return layoutConstraint
}

View File

@ -11,6 +11,8 @@ import Foundation
typealias Accumulator = ([NSLayoutConstraint], LayoutProxy)
private func reduce(first: LayoutProxy, rest: [LayoutProxy], combine: (LayoutProxy, LayoutProxy) -> NSLayoutConstraint) -> [NSLayoutConstraint] {
rest.last?.view.car_translatesAutoresizingMaskIntoConstraints = false
return reduce(rest, ([], first)) { (acc, current) -> Accumulator in
var (constraints, previous) = acc
@ -18,14 +20,44 @@ private func reduce(first: LayoutProxy, rest: [LayoutProxy], combine: (LayoutPro
}.0
}
/// Distributes multiple views horizontally.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :param: amount The distance between the views.
/// :param: views The views to distribute.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func distribute(by amount: Double, horizontally first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return reduce(first, rest) { $0.trailing + amount == $1.leading }
return reduce(first, rest) { $0.trailing == $1.leading - amount }
}
/// Distributes multiple views horizontally from left to right.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :param: amount The distance between the views.
/// :param: views The views to distribute.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func distribute(by amount: Double, leftToRight first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return reduce(first, rest) { $0.right + amount == $1.left }
return reduce(first, rest) { $0.right == $1.left - amount }
}
/// Distributes multiple views vertically.
///
/// All views passed to this function will have
/// their `translatesAutoresizingMaskIntoConstraints` properties set to `false`.
///
/// :param: amount The distance between the views.
/// :param: views The views to distribute.
///
/// :returns: An array of `NSLayoutConstraint` instances.
///
public func distribute(by amount: Double, vertically first: LayoutProxy, rest: LayoutProxy...) -> [NSLayoutConstraint] {
return reduce(first, rest) { $0.bottom + amount == $1.top }
return reduce(first, rest) { $0.bottom == $1.top - amount }
}

View File

@ -35,14 +35,41 @@ public enum Edges : Compound {
}
}
/// Insets all edges.
///
/// :param: edges The edges to inset.
/// :param: all The amount by which to inset all edges, in points.
///
/// :returns: A new expression with the inset edges.
///
public func inset(edges: Edges, all: Number) -> Expression<Edges> {
return inset(edges, all, all, all, all)
}
/// Insets the horizontal and vertical edges.
///
/// :param: edges The edges to inset.
/// :param: horizontal The amount by which to inset the horizontal edges, in
/// points.
/// :param: vertical The amount by which to inset the vertical edges, in
/// points.
///
/// :returns: A new expression with the inset edges.
///
public func inset(edges: Edges, horizontal: Number, vertical: Number) -> Expression<Edges> {
return inset(edges, vertical, horizontal, vertical, horizontal)
}
/// Insets edges individually.
///
/// :param: edges The edges to inset.
/// :param: top The amount by which to inset the top edge, in points.
/// :param: leading The amount by which to inset the leading edge, in points.
/// :param: bottom The amount by which to inset the bottom edge, in points.
/// :param: trailing The amount by which to inset the trailing edge, in points.
///
/// :returns: A new expression with the inset edges.
///
public func inset(edges: Edges, top: Number, leading: Number, bottom: Number, trailing: Number) -> Expression<Edges> {
return Expression(edges, [
Coefficients(1, top.doubleValue),
@ -54,40 +81,76 @@ public func inset(edges: Edges, top: Number, leading: Number, bottom: Number, tr
// MARK: Equality
/// Declares a property equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func == (lhs: Edges, rhs: Expression<Edges>) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value)
}
public func == (lhs: Expression<Edges>, rhs: Edges) -> [NSLayoutConstraint] {
return rhs == lhs
}
/// Declares a property equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func == (lhs: Edges, rhs: Edges) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, to: rhs)
}
// MARK: Inequality
/// Declares a property less than or equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func <= (lhs: Edges, rhs: Edges) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.LessThanOrEqual)
}
/// Declares a property greater than or equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func >= (lhs: Edges, rhs: Edges) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.GreaterThanOrEqual)
}
/// Declares a property less than or equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func <= (lhs: Edges, rhs: Expression<Edges>) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value, relation: NSLayoutRelation.LessThanOrEqual)
}
public func <= (lhs: Expression<Edges>, rhs: Edges) -> [NSLayoutConstraint] {
return rhs >= lhs
}
/// Declares a property greater than or equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func >= (lhs: Edges, rhs: Expression<Edges>) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value, relation: NSLayoutRelation.GreaterThanOrEqual)
}
public func >= (lhs: Expression<Edges>, rhs: Edges) -> [NSLayoutConstraint] {
return rhs <= lhs
}

View File

@ -8,6 +8,14 @@
import Foundation
/// Layouts a single view.
///
/// The views will have its layout updated after this call.
///
/// :param: view The view to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for `view`.
///
public func layout(view: View, replace group: ConstraintGroup = ConstraintGroup(), block: LayoutProxy -> ()) -> ConstraintGroup {
let context = Context()
block(LayoutProxy(context, view))
@ -16,22 +24,49 @@ public func layout(view: View, replace group: ConstraintGroup = ConstraintGroup(
return group
}
public func layout(v1: View, v2: View, replace group: ConstraintGroup = ConstraintGroup(), block: (LayoutProxy, LayoutProxy) -> ()) -> ConstraintGroup {
/// Layouts two views.
///
/// The views will have their layout updated after this call.
///
/// :param: view1 A view to layout.
/// :param: view2 A view to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for the views.
///
public func layout(view1: View, view2: View, replace group: ConstraintGroup = ConstraintGroup(), block: (LayoutProxy, LayoutProxy) -> ()) -> ConstraintGroup {
let context = Context()
block(LayoutProxy(context, v1), LayoutProxy(context, v2))
block(LayoutProxy(context, view1), LayoutProxy(context, view2))
group.replaceConstraints(context.constraints, performLayout: true)
return group
}
public func layout(v1: View, v2: View, v3: View, replace group: ConstraintGroup = ConstraintGroup(), block: (LayoutProxy, LayoutProxy, LayoutProxy) -> ()) -> ConstraintGroup {
/// Layouts three views.
///
/// The views will have their layout updated after this call.
///
/// :param: view1 A view to layout.
/// :param: view2 A view to layout.
/// :param: view3 A view to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for the views.
///
public func layout(view1: View, view2: View, view3: View, replace group: ConstraintGroup = ConstraintGroup(), block: (LayoutProxy, LayoutProxy, LayoutProxy) -> ()) -> ConstraintGroup {
let context = Context()
block(LayoutProxy(context, v1), LayoutProxy(context, v2), LayoutProxy(context, v3))
block(LayoutProxy(context, view1), LayoutProxy(context, view2), LayoutProxy(context, view3))
group.replaceConstraints(context.constraints, performLayout: true)
return group
}
/// Layouts an array of views.
///
/// The views will have their layout updated after this call.
///
/// :param: views The views to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for `views`.
///
public func layout(views: [View], replace group: ConstraintGroup = ConstraintGroup(), block: ([LayoutProxy]) -> ()) -> ConstraintGroup {
let context = Context()
block(views.map({ LayoutProxy(context, $0) }))
@ -40,6 +75,14 @@ public func layout(views: [View], replace group: ConstraintGroup = ConstraintGro
return group
}
/// Layouts a dictioary of views.
///
/// The views will have their layout updated after this call.
///
/// :param: views The views to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for `views`.
///
public func layout<T: Hashable>(views: [T: View], replace group: ConstraintGroup = ConstraintGroup(), block: ([T : LayoutProxy] -> ())) -> ConstraintGroup {
let context = Context()
let proxies = map(views) { ($0, LayoutProxy(context, $1)) }
@ -49,10 +92,22 @@ public func layout<T: Hashable>(views: [T: View], replace group: ConstraintGroup
return group
}
/// Removes all constraints for a group.
///
/// The affected views will have their layout updated after this call.
///
/// :param: clear The `ConstraintGroup` whose constraints should be removed.
///
public func layout(clear group: ConstraintGroup) {
group.replaceConstraints([], performLayout: true)
}
/// Updates the constraints of a single view.
///
/// :param: view The view to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for `view`.
///
public func constrain(view: View, replace group: ConstraintGroup = ConstraintGroup(), block: LayoutProxy -> ()) -> ConstraintGroup {
let context = Context()
block(LayoutProxy(context, view))
@ -61,6 +116,13 @@ public func constrain(view: View, replace group: ConstraintGroup = ConstraintGro
return group
}
/// Updates the constraints of two views.
///
/// :param: view1 A view to layout.
/// :param: view2 A view to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for the views.
///
public func constrain(v1: View, v2: View, replace group: ConstraintGroup = ConstraintGroup(), block: (LayoutProxy, LayoutProxy) -> ()) -> ConstraintGroup {
let context = Context()
block(LayoutProxy(context, v1), LayoutProxy(context, v2))
@ -69,6 +131,14 @@ public func constrain(v1: View, v2: View, replace group: ConstraintGroup = Const
return group
}
/// Updates the constraints of three views.
///
/// :param: view1 A view to layout.
/// :param: view2 A view to layout.
/// :param: view3 A view to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for the views.
///
public func constrain(v1: View, v2: View, v3: View, replace group: ConstraintGroup = ConstraintGroup(), block: (LayoutProxy, LayoutProxy, LayoutProxy) -> ()) -> ConstraintGroup {
let context = Context()
block(LayoutProxy(context, v1), LayoutProxy(context, v2), LayoutProxy(context, v3))
@ -77,6 +147,12 @@ public func constrain(v1: View, v2: View, v3: View, replace group: ConstraintGro
return group
}
/// Updates the constraints of an array of views.
///
/// :param: views The views to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for `views`.
///
public func constrain(views: [View], replace group: ConstraintGroup = ConstraintGroup(), block: ([LayoutProxy]) -> ()) -> ConstraintGroup {
let context = Context()
block(views.map({ LayoutProxy(context, $0) }))
@ -85,6 +161,12 @@ public func constrain(views: [View], replace group: ConstraintGroup = Constraint
return group
}
/// Updates the constraints of a dictionary of views.
///
/// :param: views The views to layout.
/// :param: replace The `ConstraintGroup` whose constraints should be replaced.
/// :param: block A block that declares the layout for `views`.
///
public func constrain<T: Hashable>(views: [T: View], replace group: ConstraintGroup = ConstraintGroup(), block: ([T : LayoutProxy] -> ())) -> ConstraintGroup {
let context = Context()
let proxies = map(views) { ($0, LayoutProxy(context, $1)) }
@ -94,6 +176,10 @@ public func constrain<T: Hashable>(views: [T: View], replace group: ConstraintGr
return group
}
/// Removes all constraints for a group.
///
/// :param: clear The `ConstraintGroup` whose constraints should be removed.
///
public func constrain(clear group: ConstraintGroup) {
group.replaceConstraints([], performLayout: false)
}

View File

@ -9,30 +9,54 @@
import Foundation
public class LayoutProxy {
/// The width of the view.
public let width: Dimension
/// The height of the view.
public let height: Dimension
/// The size of the view. This property affects both `width` and `height`.
public let size: Size
/// The top edge of the view.
public let top: Edge
/// The right edge of the view.
public let right: Edge
/// The bottom edge of the view.
public let bottom: Edge
/// The left edge of the view.
public let left: Edge
/// All edges of the view. This property affects `top`, `bottom`, `leading`
/// and `trailing`.
public let edges: Edges
/// The leading edge of the view.
public let leading: Edge
/// The trailing edge of the view.
public let trailing: Edge
/// The horizontal center of the view.
public let centerX: Edge
/// The vertical center of the view.
public let centerY: Edge
/// The center point of the view. This property affects `centerX` and
/// `centerY`.
public let center: Point
/// The baseline of the view.
public let baseline: Edge
internal let context: Context
internal let view: View
/// The superview of the view, if it exists.
public var superview: LayoutProxy? {
if let superview = view.superview {
return LayoutProxy(context, superview)

View File

@ -32,40 +32,76 @@ public enum Point: Compound {
// MARK: Equality
/// Declares a property equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func == (lhs: Point, rhs: Expression<Point>) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value)
}
public func == (lhs: Expression<Point>, rhs: Point) -> [NSLayoutConstraint] {
return rhs == lhs
}
/// Declares a property equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func == (lhs: Point, rhs: Point) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, to: rhs)
}
// MARK: Inequality
/// Declares a property less than or equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func <= (lhs: Point, rhs: Point) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.LessThanOrEqual)
}
/// Declares a property greater than or equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func >= (lhs: Point, rhs: Point) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.GreaterThanOrEqual)
}
/// Declares a property less than or equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func <= (lhs: Point, rhs: Expression<Point>) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value, relation: NSLayoutRelation.LessThanOrEqual)
}
public func <= (lhs: Expression<Point>, rhs: Point) -> [NSLayoutConstraint] {
return rhs >= lhs
}
/// Declares a property greater than or equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func >= (lhs: Point, rhs: Expression<Point>) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value, relation: NSLayoutRelation.GreaterThanOrEqual)
}
public func >= (lhs: Expression<Point>, rhs: Point) -> [NSLayoutConstraint] {
return rhs <= lhs
}

View File

@ -8,19 +8,37 @@
#if os(iOS)
import UIKit
public typealias LayoutPriority = UILayoutPriority
#else
import AppKit
public typealias LayoutPriority = NSLayoutPriority
#endif
infix operator ~ { }
public func ~ (lhs: NSLayoutConstraint, rhs: Float) -> NSLayoutConstraint {
/// Sets the priority for a constraint.
///
/// :param: lhs The constraint to update.
/// :param: rhs The new priority.
///
/// :returns: The same constraint with its priority updated.
///
public func ~ (lhs: NSLayoutConstraint, rhs: LayoutPriority) -> NSLayoutConstraint {
lhs.priority = rhs
return lhs
}
public func ~ (lhs: [NSLayoutConstraint], rhs: Float) -> [NSLayoutConstraint] {
/// Sets the priority for multiple constraints.
///
/// :param: lhs An array of `NSLayoutConstraint` instances.
/// :param: rhs The new priority.
///
/// :returns: The same constraints with their priorities updated.
///
public func ~ (lhs: [NSLayoutConstraint], rhs: LayoutPriority) -> [NSLayoutConstraint] {
return lhs.map {
$0 ~ rhs
}

View File

@ -24,26 +24,40 @@ public protocol Property {
/// numerical constants.
public protocol NumericalEquality : Property { }
/// Declares a property equal to a numerical constant.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The numerical constant.
///
/// :returns: An `NSLayoutConstraint`.
///
public func == (lhs: NumericalEquality, rhs: Number) -> NSLayoutConstraint {
return lhs.context.addConstraint(lhs, coefficients: Coefficients(1, rhs))
}
public func == (lhs: Number, rhs: NumericalEquality) -> NSLayoutConstraint {
return rhs == lhs
}
/// Properties conforming to this protocol can use the `==` operator with other
/// properties of the same type.
public protocol RelativeEquality : Property { }
/// Declares a property equal to a the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func == <P: RelativeEquality>(lhs: P, rhs: Expression<P>) -> NSLayoutConstraint {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients[0], to: rhs.value)
}
public func == <P: RelativeEquality>(lhs: Expression<P>, rhs: P) -> NSLayoutConstraint {
return rhs == lhs
}
/// Declares a property equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
public func == <P: RelativeEquality>(lhs: P, rhs: P) -> NSLayoutConstraint {
return lhs.context.addConstraint(lhs, to: rhs)
}
@ -54,50 +68,82 @@ public func == <P: RelativeEquality>(lhs: P, rhs: P) -> NSLayoutConstraint {
/// with numerical constants.
public protocol NumericalInequality : Property { }
/// Declares a property less than or equal to a numerical constant.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The numerical constant.
///
/// :returns: An `NSLayoutConstraint`.
///
public func <= (lhs: NumericalInequality, rhs: Number) -> NSLayoutConstraint {
return lhs.context.addConstraint(lhs, coefficients: Coefficients(1, rhs), relation: NSLayoutRelation.LessThanOrEqual)
}
public func <= (lhs: Number, rhs: NumericalInequality) -> NSLayoutConstraint {
return rhs >= lhs
}
/// Declares a property greater than or equal to a numerical constant.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The numerical constant.
///
/// :returns: An `NSLayoutConstraint`.
///
public func >= (lhs: NumericalInequality, rhs: Number) -> NSLayoutConstraint {
return lhs.context.addConstraint(lhs, coefficients: Coefficients(1, rhs), relation: NSLayoutRelation.GreaterThanOrEqual)
}
public func >= (lhs: Number, rhs: NumericalInequality) -> NSLayoutConstraint {
return rhs <= lhs
}
/// Properties conforming to this protocol can use the `<=` and `>=` operators
/// with other properties of the same type.
public protocol RelativeInequality : Property { }
/// Declares a property less than or equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func <= <P: RelativeInequality>(lhs: P, rhs: P) -> NSLayoutConstraint {
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.LessThanOrEqual)
}
/// Declares a property greater than or equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func >= <P: RelativeInequality>(lhs: P, rhs: P) -> NSLayoutConstraint {
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.GreaterThanOrEqual)
}
/// Declares a property less than or equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func <= <P: RelativeInequality>(lhs: P, rhs: Expression<P>) -> NSLayoutConstraint {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients[0], to: rhs.value, relation: NSLayoutRelation.LessThanOrEqual)
}
public func <= <P: RelativeInequality>(lhs: Expression<P>, rhs: P) -> NSLayoutConstraint {
return rhs >= lhs
}
/// Declares a property greater than or equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func >= <P: RelativeInequality>(lhs: P, rhs: Expression<P>) -> NSLayoutConstraint {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients[0], to: rhs.value, relation: NSLayoutRelation.GreaterThanOrEqual)
}
public func >= <P: RelativeInequality>(lhs: Expression<P>, rhs: P) -> NSLayoutConstraint {
return rhs <= lhs
}
// Mark: Addition
public protocol Addition : Property { }
@ -154,18 +200,10 @@ public func * <P: Multiplication>(lhs: P, rhs: Number) -> Expression<P> {
return rhs * lhs
}
public func / <P: Multiplication>(m: Number, rhs: Expression<P>) -> Expression<P> {
return Expression(rhs.value, rhs.coefficients.map { $0 / m })
}
public func / <P: Multiplication>(lhs: Expression<P>, rhs: Number) -> Expression<P> {
return rhs / lhs
}
public func / <P: Multiplication>(m: Number, rhs: P) -> Expression<P> {
return Expression(rhs, [ Coefficients(1 / m.doubleValue, 0) ])
return lhs * (1 / rhs.doubleValue)
}
public func / <P: Multiplication>(lhs: P, rhs: Number) -> Expression<P> {
return rhs / lhs
return lhs * (1 / rhs.doubleValue)
}

View File

@ -32,44 +32,80 @@ public enum Size : Compound {
// MARK: Equality
/// Declares a property equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func == (lhs: Size, rhs: Expression<Size>) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value)
}
public func == (lhs: Expression<Size>, rhs: Size) -> [NSLayoutConstraint] {
return rhs == lhs
}
/// Declares a property equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func == (lhs: Size, rhs: Size) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, to: rhs)
}
// MARK: Inequality
/// Declares a property less than or equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func <= (lhs: Size, rhs: Size) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.LessThanOrEqual)
}
/// Declares a property greater than or equal to another property.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The other property.
///
/// :returns: An `NSLayoutConstraint`.
///
public func >= (lhs: Size, rhs: Size) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, to: rhs, relation: NSLayoutRelation.GreaterThanOrEqual)
}
/// Declares a property less than or equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func <= (lhs: Size, rhs: Expression<Size>) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value, relation: NSLayoutRelation.LessThanOrEqual)
}
public func <= (lhs: Expression<Size>, rhs: Size) -> [NSLayoutConstraint] {
return rhs >= lhs
}
/// Declares a property greater than or equal to the result of an expression.
///
/// :param: lhs The affected property. The associated view will have
/// `translatesAutoresizingMaskIntoConstraints` set to `false`.
/// :param: rhs The expression.
///
/// :returns: An `NSLayoutConstraint`.
///
public func >= (lhs: Size, rhs: Expression<Size>) -> [NSLayoutConstraint] {
return lhs.context.addConstraint(lhs, coefficients: rhs.coefficients, to: rhs.value, relation: NSLayoutRelation.GreaterThanOrEqual)
}
public func >= (lhs: Expression<Size>, rhs: Size) -> [NSLayoutConstraint] {
return rhs <= lhs
}
// MARK: Multiplication
public func * (m: Number, rhs: Expression<Size>) -> Expression<Size> {
@ -90,18 +126,10 @@ public func * (lhs: Size, rhs: Number) -> Expression<Size> {
// MARK: Division
public func / (m: Number, rhs: Expression<Size>) -> Expression<Size> {
return Expression(rhs.value, rhs.coefficients.map { $0 / m.doubleValue })
}
public func / (lhs: Expression<Size>, rhs: Number) -> Expression<Size> {
return rhs / lhs
}
public func / (m: Number, rhs: Size) -> Expression<Size> {
return Expression(rhs, [ Coefficients(1 / m.doubleValue, 0), Coefficients(1 / m.doubleValue, 0) ])
return lhs * (1 / rhs.doubleValue)
}
public func / (lhs: Size, rhs: Number) -> Expression<Size> {
return rhs / lhs
return lhs * (1 / rhs.doubleValue)
}

View File

@ -17,28 +17,9 @@ import Foundation
layoutIfNeeded()
}
func car_disableTranslatesAutoresizingMaskIntoConstraintsIfPossible() {
if !car_isOwnedByViewController {
setTranslatesAutoresizingMaskIntoConstraints(false)
}
}
private var car_isOwnedByViewController: Bool {
return self === car_closestViewController?.view
}
private var car_closestViewController: UIViewController? {
var responder: UIResponder = self
while let nextResponder = nextResponder() {
if nextResponder === responder { break }
responder = nextResponder
if nextResponder.isKindOfClass(UIViewController.Type) { break }
}
return responder as? UIViewController
public var car_translatesAutoresizingMaskIntoConstraints: Bool {
get { return translatesAutoresizingMaskIntoConstraints() }
set { setTranslatesAutoresizingMaskIntoConstraints(newValue) }
}
}
#else
@ -50,8 +31,9 @@ import Foundation
(superview ?? self).layoutSubtreeIfNeeded()
}
func car_disableTranslatesAutoresizingMaskIntoConstraintsIfPossible() {
translatesAutoresizingMaskIntoConstraints = false
public var car_translatesAutoresizingMaskIntoConstraints: Bool {
get { return translatesAutoresizingMaskIntoConstraints }
set { translatesAutoresizingMaskIntoConstraints = newValue }
}
}
#endif
@ -67,4 +49,4 @@ extension View {
objc_setAssociatedObject(self, &InstalledLayoutConstraintsKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_COPY_NONATOMIC))
}
}
}
}

View File

@ -6,14 +6,12 @@
// Copyright (c) 2014 Robert Böhnke. All rights reserved.
//
#if os(iOS) && TEST
#if TEST
import Cartography
#elseif os(iOS)
import UIKit
typealias View = UIView
#elseif TEST
import AppKit
typealias View = NSView
#else
import Foundation
import AppKit
#endif
func closestCommonAncestor(a: View, b: View) -> View? {

View File

@ -46,6 +46,10 @@ class AlignTests: XCTestCase {
XCTAssertEqual(viewA.frame, viewB.frame, "It should align the edges")
XCTAssertEqual(viewA.frame, viewC.frame, "It should align the edges")
XCTAssertFalse(viewA.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewB.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewC.car_translatesAutoresizingMaskIntoConstraints)
}
func testAlignCenter() {
@ -59,5 +63,9 @@ class AlignTests: XCTestCase {
XCTAssertEqual(viewA.frame, viewB.frame, "It should align the edges")
XCTAssertEqual(viewA.frame, viewC.frame, "It should align the edges")
XCTAssertFalse(viewA.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewB.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewC.car_translatesAutoresizingMaskIntoConstraints)
}
}

View File

@ -34,8 +34,8 @@ class DistributeTests: XCTestCase {
viewA.top == viewA.superview!.top
viewA.left == viewA.superview!.left
viewB.size == viewA.size
viewC.size == viewA.size
viewA.size == viewB.size
viewA.size == viewC.size
}
}
@ -48,6 +48,10 @@ class DistributeTests: XCTestCase {
XCTAssertEqual(viewA.frame, CGRect(x: 0, y: 0, width: 100, height: 100), "It should distribute the views")
XCTAssertEqual(viewB.frame, CGRect(x: 110, y: 0, width: 100, height: 100), "It should distribute the views")
XCTAssertEqual(viewC.frame, CGRect(x: 220, y: 0, width: 100, height: 100), "It should distribute the views")
XCTAssertFalse(viewA.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewB.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewC.car_translatesAutoresizingMaskIntoConstraints)
}
func testDistributeLeftToRight() {
@ -59,6 +63,10 @@ class DistributeTests: XCTestCase {
XCTAssertEqual(viewA.frame, CGRect(x: 0, y: 0, width: 100, height: 100), "It should distribute the views")
XCTAssertEqual(viewB.frame, CGRect(x: 110, y: 0, width: 100, height: 100), "It should distribute the views")
XCTAssertEqual(viewC.frame, CGRect(x: 220, y: 0, width: 100, height: 100), "It should distribute the views")
XCTAssertFalse(viewA.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewB.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewC.car_translatesAutoresizingMaskIntoConstraints)
}
func testDistributeVertically() {
@ -70,6 +78,9 @@ class DistributeTests: XCTestCase {
XCTAssertEqual(viewA.frame, CGRect(x: 0, y: 0, width: 100, height: 100), "It should distribute the views")
XCTAssertEqual(viewB.frame, CGRect(x: 0, y: 110, width: 100, height: 100), "It should distribute the views")
XCTAssertEqual(viewC.frame, CGRect(x: 0, y: 220, width: 100, height: 100), "It should distribute the views")
XCTAssertFalse(viewA.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewB.car_translatesAutoresizingMaskIntoConstraints)
XCTAssertFalse(viewC.car_translatesAutoresizingMaskIntoConstraints)
}
}

View File

@ -55,7 +55,7 @@ class ViewHierarchyTests: XCTestCase {
XCTAssert(testBothWays(viewA, viewAGrandparent) == viewAGrandparent, "It should handle a view and its grandparent")
}
func testSharedSuperTestView() {
func testSharedSuperview() {
viewB.removeFromSuperview()
viewAParent.addSubview(viewB)
XCTAssert(testBothWays(viewA, viewB) == viewAParent, "It should handle two views with the same superview")

View File

@ -33,6 +33,13 @@ layout(view1, view2) { view1, view2 in
}
```
For every view on the left hand side of an equality or inequality operator,
Cartography will automatically set its
`translatesAutoresizingMaskIntoConstraints` property to `false`. If the view is
not controlled by youfor example if it belongs to a Apple-provided
`UIViewController` classyou should take appropriate care when declaring its
constraints.
Note that `layout` will automatically relayout the views as necessary. If you
instead want to trigger the layouting step yourself, you can instead use the
`constrain` function: