Bläddra i källkod

Improve debugging and allow width/height constraints to be installed on from view

Robert Payne 10 år sedan
förälder
incheckning
9ef9ae6f9f
4 ändrade filer med 76 tillägg och 35 borttagningar
  1. 16 10
      Source/Constraint.swift
  2. 27 12
      Source/ConstraintMaker.swift
  3. 24 4
      Source/Debugging.swift
  4. 9 9
      Source/View+SnapKit.swift

+ 16 - 10
Source/Constraint.swift

@@ -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
+    
 }
 
 /**
@@ -191,23 +194,26 @@ 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 []
                 }
             }
@@ -215,7 +221,7 @@ internal class ConcreteConstraint: Constraint {
         
         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] ?? []

+ 27 - 12
Source/ConstraintMaker.swift

@@ -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]()
     
@@ -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()
         }

+ 24 - 4
Source/Debugging.swift

@@ -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 {

+ 9 - 9
Source/View+SnapKit.swift

@@ -124,8 +124,8 @@ 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)
     }
     
     /**
@@ -133,8 +133,8 @@ public extension View {
         
         :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)
     }
     
     /**
@@ -144,8 +144,8 @@ 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)
     }
     
     /**
@@ -153,15 +153,15 @@ public extension View {
     
         :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] {