diff --git a/Snap/Constraint.swift b/Snap/Constraint.swift index 9a07b07..7052ab3 100644 --- a/Snap/Constraint.swift +++ b/Snap/Constraint.swift @@ -31,19 +31,24 @@ import AppKit * Constraint is a single item that defines all the properties for a single ConstraintMaker chain */ public class Constraint { - public var left: Constraint { return addConstraint(ConstraintAttributes.Left) } - public var top: Constraint { return addConstraint(ConstraintAttributes.Top) } - public var right: Constraint { return addConstraint(ConstraintAttributes.Right) } - public var bottom: Constraint { return addConstraint(ConstraintAttributes.Bottom) } - public var leading: Constraint { return addConstraint(ConstraintAttributes.Leading) } - public var trailing: Constraint { return addConstraint(ConstraintAttributes.Trailing) } - public var width: Constraint { return addConstraint(ConstraintAttributes.Width) } - public var height: Constraint { return addConstraint(ConstraintAttributes.Height) } - public var centerX: Constraint { return addConstraint(ConstraintAttributes.CenterX) } - public var centerY: Constraint { return addConstraint(ConstraintAttributes.CenterY) } - public var baseline: Constraint { return addConstraint(ConstraintAttributes.Baseline) } + public var left: Constraint { return self.addConstraint(ConstraintAttributes.Left) } + public var top: Constraint { return self.addConstraint(ConstraintAttributes.Top) } + public var right: Constraint { return self.addConstraint(ConstraintAttributes.Right) } + public var bottom: Constraint { return self.addConstraint(ConstraintAttributes.Bottom) } + public var leading: Constraint { return self.addConstraint(ConstraintAttributes.Leading) } + public var trailing: Constraint { return self.addConstraint(ConstraintAttributes.Trailing) } + public var width: Constraint { return self.addConstraint(ConstraintAttributes.Width) } + public var height: Constraint { return self.addConstraint(ConstraintAttributes.Height) } + public var centerX: Constraint { return self.addConstraint(ConstraintAttributes.CenterX) } + public var centerY: Constraint { return self.addConstraint(ConstraintAttributes.CenterY) } + public var baseline: Constraint { return self.addConstraint(ConstraintAttributes.Baseline) } - public var and: Constraint { return self } + public var and: Constraint { + if self.relation != nil { + fatalError("And is semantic only and can only be used before a relation is set.") + } + return self + } public var with: Constraint { return self } // MARK: initializer @@ -56,115 +61,115 @@ public class Constraint { // MARK: equalTo public func equalTo(other: ConstraintItem) -> Constraint { - return constrainTo(other, relation: .Equal) + return self.constrainTo(other, relation: .Equal) } public func equalTo(other: View) -> Constraint { - return constrainTo(other, relation: .Equal) + return self.constrainTo(other, relation: .Equal) } #if os(iOS) public func equalTo(other: UILayoutSupport) -> Constraint { - return constrainTo(other, relation: .Equal) + return self.constrainTo(other, relation: .Equal) } #endif public func equalTo(other: Float) -> Constraint { - return constrainTo(other, relation: .Equal) + return self.constrainTo(other, relation: .Equal) } public func equalTo(other: Double) -> Constraint { - return constrainTo(Float(other), relation: .Equal) + return self.constrainTo(Float(other), relation: .Equal) } public func equalTo(other: CGFloat) -> Constraint { - return constrainTo(Float(other), relation: .Equal) + return self.constrainTo(Float(other), relation: .Equal) } public func equalTo(other: Int) -> Constraint { - return constrainTo(Float(other), relation: .Equal) + return self.constrainTo(Float(other), relation: .Equal) } public func equalTo(other: UInt) -> Constraint { - return constrainTo(Float(other), relation: .Equal) + return self.constrainTo(Float(other), relation: .Equal) } public func equalTo(other: CGSize) -> Constraint { - return constrainTo(other, relation: .Equal) + return self.constrainTo(other, relation: .Equal) } public func equalTo(other: CGPoint) -> Constraint { - return constrainTo(other, relation: .Equal) + return self.constrainTo(other, relation: .Equal) } public func equalTo(other: EdgeInsets) -> Constraint { - return constrainTo(other, relation: .Equal) + return self.constrainTo(other, relation: .Equal) } // MARK: lessThanOrEqualTo public func lessThanOrEqualTo(other: ConstraintItem) -> Constraint { - return constrainTo(other, relation: .LessThanOrEqualTo) + return self.constrainTo(other, relation: .LessThanOrEqualTo) } public func lessThanOrEqualTo(other: View) -> Constraint { - return constrainTo(other, relation: .LessThanOrEqualTo) + return self.constrainTo(other, relation: .LessThanOrEqualTo) } #if os(iOS) public func lessThanOrEqualTo(other: UILayoutSupport) -> Constraint { - return constrainTo(other, relation: .LessThanOrEqualTo) + return self.constrainTo(other, relation: .LessThanOrEqualTo) } #endif public func lessThanOrEqualTo(other: Float) -> Constraint { - return constrainTo(other, relation: .LessThanOrEqualTo) + return self.constrainTo(other, relation: .LessThanOrEqualTo) } public func lessThanOrEqualTo(other: Double) -> Constraint { - return constrainTo(Float(other), relation: .LessThanOrEqualTo) + return self.constrainTo(Float(other), relation: .LessThanOrEqualTo) } public func lessThanOrEqualTo(other: CGFloat) -> Constraint { - return constrainTo(Float(other), relation: .LessThanOrEqualTo) + return self.constrainTo(Float(other), relation: .LessThanOrEqualTo) } public func lessThanOrEqualTo(other: Int) -> Constraint { - return constrainTo(Float(other), relation: .LessThanOrEqualTo) + return self.constrainTo(Float(other), relation: .LessThanOrEqualTo) } public func lessThanOrEqualTo(other: UInt) -> Constraint { - return constrainTo(Float(other), relation: .LessThanOrEqualTo) + return self.constrainTo(Float(other), relation: .LessThanOrEqualTo) } public func lessThanOrEqualTo(other: CGSize) -> Constraint { - return constrainTo(other, relation: .LessThanOrEqualTo) + return self.constrainTo(other, relation: .LessThanOrEqualTo) } public func lessThanOrEqualTo(other: CGPoint) -> Constraint { - return constrainTo(other, relation: .LessThanOrEqualTo) + return self.constrainTo(other, relation: .LessThanOrEqualTo) } public func lessThanOrEqualTo(other: EdgeInsets) -> Constraint { - return constrainTo(other, relation: .LessThanOrEqualTo) + return self.constrainTo(other, relation: .LessThanOrEqualTo) } // MARK: greaterThanOrEqualTo public func greaterThanOrEqualTo(other: ConstraintItem) -> Constraint { - return constrainTo(other, relation: .GreaterThanOrEqualTo) + return self.constrainTo(other, relation: .GreaterThanOrEqualTo) } public func greaterThanOrEqualTo(other: View) -> Constraint { - return constrainTo(other, relation: .GreaterThanOrEqualTo) + return self.constrainTo(other, relation: .GreaterThanOrEqualTo) } #if os(iOS) public func greaterThanOrEqualTo(other: UILayoutSupport) -> Constraint { - return constrainTo(other, relation: .GreaterThanOrEqualTo) + return self.constrainTo(other, relation: .GreaterThanOrEqualTo) } #endif public func greaterThanOrEqualTo(other: Float) -> Constraint { - return constrainTo(other, relation: .GreaterThanOrEqualTo) + return self.constrainTo(other, relation: .GreaterThanOrEqualTo) } public func greaterThanOrEqualTo(other: Double) -> Constraint { - return constrainTo(Float(other), relation: .GreaterThanOrEqualTo) + return self.constrainTo(Float(other), relation: .GreaterThanOrEqualTo) } public func greaterThanOrEqualTo(other: CGFloat) -> Constraint { - return constrainTo(Float(other), relation: .GreaterThanOrEqualTo) + return self.constrainTo(Float(other), relation: .GreaterThanOrEqualTo) } public func greaterThanOrEqualTo(other: Int) -> Constraint { - return constrainTo(Float(other), relation: .GreaterThanOrEqualTo) + return self.constrainTo(Float(other), relation: .GreaterThanOrEqualTo) } public func greaterThanOrEqualTo(other: UInt) -> Constraint { - return constrainTo(Float(other), relation: .GreaterThanOrEqualTo) + return self.constrainTo(Float(other), relation: .GreaterThanOrEqualTo) } public func greaterThanOrEqualTo(other: CGSize) -> Constraint { - return constrainTo(other, relation: .GreaterThanOrEqualTo) + return self.constrainTo(other, relation: .GreaterThanOrEqualTo) } public func greaterThanOrEqualTo(other: CGPoint) -> Constraint { - return constrainTo(other, relation: .GreaterThanOrEqualTo) + return self.constrainTo(other, relation: .GreaterThanOrEqualTo) } public func greaterThanOrEqualTo(other: EdgeInsets) -> Constraint { - return constrainTo(other, relation: .GreaterThanOrEqualTo) + return self.constrainTo(other, relation: .GreaterThanOrEqualTo) } // MARK: multiplier @@ -308,7 +313,7 @@ public class Constraint { } } - var layoutConstraints: Array = [] + var newLayoutConstraints = Array() let layoutFromAttributes = self.fromItem.attributes.layoutAttributes let layoutToAttributes = self.toItem.attributes.layoutAttributes @@ -348,7 +353,7 @@ public class Constraint { // set constraint layoutConstraint.constraint = self - layoutConstraints.append(layoutConstraint) + newLayoutConstraints.append(layoutConstraint) } // special logic for updating @@ -356,11 +361,11 @@ public class Constraint { // get existing constraints for this view let existingLayoutConstraints = reverse(layoutFrom!.snp_installedLayoutConstraints) - // array that will contain only new layout constraints - var newLayoutConstraints = Array() - + // array that will contain only new layout constraints to keep + var newLayoutConstraintsToKeep = Array() + // begin looping - for layoutConstraint in layoutConstraints { + for layoutConstraint in newLayoutConstraints { // layout constraint that should be updated var updateLayoutConstraint: LayoutConstraint? = nil @@ -376,26 +381,35 @@ public class Constraint { if updateLayoutConstraint != nil { updateLayoutConstraint!.constant = layoutConstraint.constant } - // otherwise add this layout constraint to new list + // otherwise add this layout constraint to new keep list else { - newLayoutConstraints.append(layoutConstraint) + newLayoutConstraintsToKeep.append(layoutConstraint) } } - + // set constraints to only new ones - layoutConstraints = newLayoutConstraints + newLayoutConstraints = newLayoutConstraintsToKeep } // add constraints - installOnView!.addConstraints(layoutConstraints) + installOnView!.addConstraints(newLayoutConstraints) + // store which view this constraint was installed on self.installedOnView = installOnView + + // store which layout constraints are installed for this constraint self.installedLayoutConstraints = NSHashTable.weakObjectsHashTable() - for layoutConstraint in layoutConstraints { + for layoutConstraint in newLayoutConstraints { self.installedLayoutConstraints!.addObject(layoutConstraint) } - return layoutConstraints + // store the layout constraints against the installed on view + var layoutConstraints = Array(layoutFrom!.snp_installedLayoutConstraints) + layoutConstraints += newLayoutConstraints + layoutFrom!.snp_installedLayoutConstraints = layoutConstraints + + // return the new constraints + return newLayoutConstraints } internal func uninstallFromView() { diff --git a/Snap/ConstraintMaker.swift b/Snap/ConstraintMaker.swift index 69aaead..49e03d2 100644 --- a/Snap/ConstraintMaker.swift +++ b/Snap/ConstraintMaker.swift @@ -31,21 +31,21 @@ import AppKit * ConstraintMaker is the maker in snap that gets all constraints kickstarted */ public class ConstraintMaker { - public var left: Constraint { return addConstraint(ConstraintAttributes.Left) } - public var top: Constraint { return addConstraint(ConstraintAttributes.Top) } - public var right: Constraint { return addConstraint(ConstraintAttributes.Right) } - public var bottom: Constraint { return addConstraint(ConstraintAttributes.Bottom) } - public var leading: Constraint { return addConstraint(ConstraintAttributes.Leading) } - public var trailing: Constraint { return addConstraint(ConstraintAttributes.Trailing) } - public var width: Constraint { return addConstraint(ConstraintAttributes.Width) } - public var height: Constraint { return addConstraint(ConstraintAttributes.Height) } - public var centerX: Constraint { return addConstraint(ConstraintAttributes.CenterX) } - public var centerY: Constraint { return addConstraint(ConstraintAttributes.CenterY) } - public var baseline: Constraint { return addConstraint(ConstraintAttributes.Baseline) } + public var left: Constraint { return self.addConstraint(ConstraintAttributes.Left) } + public var top: Constraint { return self.addConstraint(ConstraintAttributes.Top) } + public var right: Constraint { return self.addConstraint(ConstraintAttributes.Right) } + public var bottom: Constraint { return self.addConstraint(ConstraintAttributes.Bottom) } + public var leading: Constraint { return self.addConstraint(ConstraintAttributes.Leading) } + public var trailing: Constraint { return self.addConstraint(ConstraintAttributes.Trailing) } + public var width: Constraint { return self.addConstraint(ConstraintAttributes.Width) } + public var height: Constraint { return self.addConstraint(ConstraintAttributes.Height) } + public var centerX: Constraint { return self.addConstraint(ConstraintAttributes.CenterX) } + public var centerY: Constraint { return self.addConstraint(ConstraintAttributes.CenterY) } + public var baseline: Constraint { return self.addConstraint(ConstraintAttributes.Baseline) } - public var edges: Constraint { return addConstraint(ConstraintAttributes.Edges) } - public var size: Constraint { return addConstraint(ConstraintAttributes.Size) } - public var center: Constraint { return addConstraint(ConstraintAttributes.Center) } + public var edges: Constraint { return self.addConstraint(ConstraintAttributes.Edges) } + public var size: Constraint { return self.addConstraint(ConstraintAttributes.Size) } + public var center: Constraint { return self.addConstraint(ConstraintAttributes.Center) } init(view: View) { self.view = view @@ -61,6 +61,12 @@ public class ConstraintMaker { return constraint } + internal class func prepareConstraints(view: View, block: (make: ConstraintMaker) -> Void) -> Array { + let maker = ConstraintMaker(view: view) + block(make: maker) + return maker.constraints + } + internal class func makeConstraints(view: View, block: (make: ConstraintMaker) -> Void) { #if os(iOS) view.setTranslatesAutoresizingMaskIntoConstraints(false) @@ -69,13 +75,9 @@ public class ConstraintMaker { #endif let maker = ConstraintMaker(view: view) block(make: maker) - - var layoutConstraints = Array(view.snp_installedLayoutConstraints) for constraint in maker.constraints { - layoutConstraints += constraint.install() + constraint.installOnView(updateExisting: false) } - - view.snp_installedLayoutConstraints = layoutConstraints } internal class func remakeConstraints(view: View, block: (make: ConstraintMaker) -> Void) { @@ -89,15 +91,12 @@ public class ConstraintMaker { var layoutConstraints = Array(view.snp_installedLayoutConstraints) for existingLayoutConstraint in layoutConstraints { - existingLayoutConstraint.constraint?.uninstall() + existingLayoutConstraint.constraint?.uninstallFromView() } - layoutConstraints = [] for constraint in maker.constraints { - layoutConstraints += constraint.install() + constraint.installOnView(updateExisting: false) } - - view.snp_installedLayoutConstraints = layoutConstraints } internal class func updateConstraints(view: View, block: (make: ConstraintMaker) -> Void) { @@ -109,20 +108,15 @@ public class ConstraintMaker { let maker = ConstraintMaker(view: view) block(make: maker) - var layoutConstraints = Array(view.snp_installedLayoutConstraints) for constraint in maker.constraints { - layoutConstraints += constraint.installOnView(updateExisting: true) + constraint.installOnView(updateExisting: true) } - - view.snp_installedLayoutConstraints = layoutConstraints } internal class func removeConstraints(view: View) { - let eixsitingLayoutConstraints = Array(view.snp_installedLayoutConstraints) - for existingLayoutConstraint in eixsitingLayoutConstraints { - existingLayoutConstraint.constraint?.uninstall() + let existingLayoutConstraints = Array(view.snp_installedLayoutConstraints) + for existingLayoutConstraint in existingLayoutConstraints { + existingLayoutConstraint.constraint?.uninstallFromView() } - - view.snp_installedLayoutConstraints = [] } } \ No newline at end of file diff --git a/Snap/View+Snap.swift b/Snap/View+Snap.swift index 7198c0f..09d1bab 100644 --- a/Snap/View+Snap.swift +++ b/Snap/View+Snap.swift @@ -68,6 +68,10 @@ public extension View { public var snp_centerWithinMargins: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.CenterWithinMargins) } #endif + public func snp_prepareConstraints(block: (make: ConstraintMaker) -> Void) -> Array { + return ConstraintMaker.prepareConstraints(self, block: block) + } + public func snp_makeConstraints(block: (make: ConstraintMaker) -> Void) { ConstraintMaker.makeConstraints(self, block: block) }