Skip to content

Commit

Permalink
Improve debugging and allow width/height constraints to be installed …
Browse files Browse the repository at this point in the history
…on from view
  • Loading branch information
robertjpayne committed Jul 30, 2015
1 parent 3d9a449 commit 9ef9ae6
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 35 deletions.
26 changes: 16 additions & 10 deletions Source/Constraint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ public class Constraint {
public func updatePriorityMedium() -> Void { fatalError("Must be implemented by Concrete subclass.") }
public func updatePriorityLow() -> Void { fatalError("Must be implemented by Concrete subclass.") }

internal var makerFile: String = "Unknown"
internal var makerLine: UInt = 0

}

/**
Expand Down Expand Up @@ -191,31 +194,34 @@ internal class ConcreteConstraint: Constraint {
self.priority = priority
}

internal func installOnView(updateExisting: Bool = false) -> [LayoutConstraint] {
internal func installOnView(updateExisting: Bool = false, file: String? = nil, line: UInt? = nil) -> [LayoutConstraint] {
var installOnView: View? = nil
if self.toItem.view != nil {
installOnView = closestCommonSuperviewBetween(self.fromItem.view, self.toItem.view)
if installOnView == nil {
NSException(name: "Cannot Install Constraint", reason: "No common superview between views", userInfo: nil).raise()
NSException(name: "Cannot Install Constraint", reason: "No common superview between views (@\(self.makerFile)#\(self.makerLine))", userInfo: nil).raise()
return []
}
} else {
installOnView = self.fromItem.view?.superview
if installOnView == nil {
if self.fromItem.attributes == ConstraintAttributes.Width || self.fromItem.attributes == ConstraintAttributes.Height {
installOnView = self.fromItem.view
}


let widthAttr = ConstraintAttributes.Width
let heightAttr = ConstraintAttributes.Height
let sizeAttrs = widthAttr | heightAttr

if self.fromItem.attributes == widthAttr || self.fromItem.attributes == heightAttr || self.fromItem.attributes == sizeAttrs {
installOnView = self.fromItem.view
} else {
installOnView = self.fromItem.view?.superview
if installOnView == nil {
NSException(name: "Cannot Install Constraint", reason: "Missing superview", userInfo: nil).raise()
NSException(name: "Cannot Install Constraint", reason: "Missing superview (@\(self.makerFile)#\(self.makerLine))", userInfo: nil).raise()
return []
}
}
}

if let installedOnView = self.installInfo?.view {
if installedOnView != installOnView {
NSException(name: "Cannot Install Constraint", reason: "Already installed on different view.", userInfo: nil).raise()
NSException(name: "Cannot Install Constraint", reason: "Already installed on different view. (@\(self.makerFile)#\(self.makerLine))", userInfo: nil).raise()
return []
}
return self.installInfo?.layoutConstraints.allObjects as? [LayoutConstraint] ?? []
Expand Down
39 changes: 27 additions & 12 deletions Source/ConstraintMaker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,14 @@ public class ConstraintMaker {

#endif

internal init(view: View) {
internal init(view: View, file: String, line: UInt) {
self.view = view
self.file = file
self.line = line
}

internal let file: String
internal let line: UInt
internal let view: View
internal var constraintDescriptions = [ConstraintDescription]()

Expand All @@ -129,60 +133,71 @@ public class ConstraintMaker {
return constraintDescription
}

internal class func prepareConstraints(view: View, @noescape closure: (make: ConstraintMaker) -> Void) -> [Constraint] {
let maker = ConstraintMaker(view: view)
internal class func prepareConstraints(#view: View, file: String = "Unknown", line: UInt = 0, @noescape closure: (make: ConstraintMaker) -> Void) -> [Constraint] {
let maker = ConstraintMaker(view: view, file: file, line: line)
closure(make: maker)

return maker.constraintDescriptions.map { $0.constraint }
let constraints = maker.constraintDescriptions.map { $0.constraint }
for constraint in constraints {
constraint.makerFile = maker.file
constraint.makerLine = maker.line
}
return constraints
}

internal class func makeConstraints(view: View, @noescape closure: (make: ConstraintMaker) -> Void) {
internal class func makeConstraints(#view: View, file: String = "Unknown", line: UInt = 0, @noescape closure: (make: ConstraintMaker) -> Void) {
#if os(iOS)
view.setTranslatesAutoresizingMaskIntoConstraints(false)
#else
view.translatesAutoresizingMaskIntoConstraints = false
#endif
let maker = ConstraintMaker(view: view)
let maker = ConstraintMaker(view: view, file: file, line: line)
closure(make: maker)

let constraints = maker.constraintDescriptions.map { $0.constraint as! ConcreteConstraint }
for constraint in constraints {
constraint.makerFile = maker.file
constraint.makerLine = maker.line
constraint.installOnView(updateExisting: false)
}
}

internal class func remakeConstraints(view: View, @noescape closure: (make: ConstraintMaker) -> Void) {
internal class func remakeConstraints(#view: View, file: String = "Unknown", line: UInt = 0, @noescape closure: (make: ConstraintMaker) -> Void) {
#if os(iOS)
view.setTranslatesAutoresizingMaskIntoConstraints(false)
#else
view.translatesAutoresizingMaskIntoConstraints = false
#endif
let maker = ConstraintMaker(view: view)
let maker = ConstraintMaker(view: view, file: file, line: line)
closure(make: maker)

self.removeConstraints(view)
self.removeConstraints(view: view)
let constraints = maker.constraintDescriptions.map { $0.constraint as! ConcreteConstraint }
for constraint in constraints {
constraint.makerFile = maker.file
constraint.makerLine = maker.line
constraint.installOnView(updateExisting: false)
}
}

internal class func updateConstraints(view: View, @noescape closure: (make: ConstraintMaker) -> Void) {
internal class func updateConstraints(#view: View, file: String = "Unknown", line: UInt = 0, @noescape closure: (make: ConstraintMaker) -> Void) {
#if os(iOS)
view.setTranslatesAutoresizingMaskIntoConstraints(false)
#else
view.translatesAutoresizingMaskIntoConstraints = false
#endif
let maker = ConstraintMaker(view: view)
let maker = ConstraintMaker(view: view, file: file, line: line)
closure(make: maker)

let constraints = maker.constraintDescriptions.map { $0.constraint as! ConcreteConstraint}
for constraint in constraints {
constraint.makerFile = maker.file
constraint.makerLine = maker.line
constraint.installOnView(updateExisting: true)
}
}

internal class func removeConstraints(view: View) {
internal class func removeConstraints(#view: View) {
for existingLayoutConstraint in view.snp_installedLayoutConstraints {
existingLayoutConstraint.snp_constraint?.uninstall()
}
Expand Down
28 changes: 24 additions & 4 deletions Source/Debugging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,38 @@ public extension LayoutConstraint {
return description
}

internal var snp_makerFile: String? {
return self.snp_constraint?.makerFile
}

internal var snp_makerLine: UInt? {
return self.snp_constraint?.makerLine
}

}

private var labelKey = ""

private func descriptionForObject(object: AnyObject) -> String {
let pointerDescription = NSString(format: "%p", [object])
let pointerDescription = NSString(format: "%p", ObjectIdentifier(object).uintValue)
var desc = ""

desc += object.dynamicType.description()

if let object = object as? View {
return "<\(object.dynamicType.description()):\(object.snp_label ?? pointerDescription)>"
desc += ":\(object.snp_label ?? pointerDescription)"
} else if let object = object as? LayoutConstraint {
return "<\(object.dynamicType.description()):\(object.snp_label ?? pointerDescription)>"
desc += ":\(object.snp_label ?? pointerDescription)"
} else {
desc += ":\(pointerDescription)"
}

if let object = object as? LayoutConstraint, let file = object.snp_makerFile, let line = object.snp_makerLine {
desc += "@\(file)#\(line)"
}
return "<\(object.dynamicType.description()):\(pointerDescription)>"

desc += ""
return desc
}

private extension NSLayoutRelation {
Expand Down
18 changes: 9 additions & 9 deletions Source/View+SnapKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,17 @@ public extension View {
:returns: the constraints made
*/
public func snp_prepareConstraints(@noescape closure: (make: ConstraintMaker) -> Void) -> [Constraint] {
return ConstraintMaker.prepareConstraints(self, closure: closure)
public func snp_prepareConstraints(file: String = __FILE__, line: UInt = __LINE__, @noescape closure: (make: ConstraintMaker) -> Void) -> [Constraint] {
return ConstraintMaker.prepareConstraints(view: self, file: file, line: line, closure: closure)
}

/**
Makes constraints with a `ConstraintMaker` and installs them along side any previous made constraints.
:param: closure that will be passed the `ConstraintMaker` to make the constraints with
*/
public func snp_makeConstraints(@noescape closure: (make: ConstraintMaker) -> Void) -> Void {
ConstraintMaker.makeConstraints(self, closure: closure)
public func snp_makeConstraints(file: String = __FILE__, line: UInt = __LINE__, @noescape closure: (make: ConstraintMaker) -> Void) -> Void {
ConstraintMaker.makeConstraints(view: self, file: file, line: line, closure: closure)
}

/**
Expand All @@ -144,24 +144,24 @@ public extension View {
:param: closure that will be passed the `ConstraintMaker` to update the constraints with
*/
public func snp_updateConstraints(@noescape closure: (make: ConstraintMaker) -> Void) -> Void {
ConstraintMaker.updateConstraints(self, closure: closure)
public func snp_updateConstraints(file: String = __FILE__, line: UInt = __LINE__, @noescape closure: (make: ConstraintMaker) -> Void) -> Void {
ConstraintMaker.updateConstraints(view: self, file: file, line: line, closure: closure)
}

/**
Remakes constraints with a `ConstraintMaker` that will first remove all previously made constraints and make and install new ones.
:param: closure that will be passed the `ConstraintMaker` to remake the constraints with
*/
public func snp_remakeConstraints(@noescape closure: (make: ConstraintMaker) -> Void) -> Void {
ConstraintMaker.remakeConstraints(self, closure: closure)
public func snp_remakeConstraints(file: String = __FILE__, line: UInt = __LINE__, @noescape closure: (make: ConstraintMaker) -> Void) -> Void {
ConstraintMaker.remakeConstraints(view: self, file: file, line: line, closure: closure)
}

/**
Removes all previously made constraints.
*/
public func snp_removeConstraints() {
ConstraintMaker.removeConstraints(self)
ConstraintMaker.removeConstraints(view: self)
}

internal var snp_installedLayoutConstraints: [LayoutConstraint] {
Expand Down

0 comments on commit 9ef9ae6

Please sign in to comment.