From dc304472aab328c97ab7acd7b798d5074ff4d3d5 Mon Sep 17 00:00:00 2001 From: Robert Payne Date: Fri, 7 Oct 2016 23:58:56 +1300 Subject: [PATCH] Begin support for LayoutGuide constraint maker --- SnapKit.xcodeproj/project.pbxproj | 4 ++ Source/Constraint.swift | 16 ++--- Source/ConstraintAttributes.swift | 2 +- Source/ConstraintDescription.swift | 8 +-- Source/ConstraintItem.swift | 6 +- Source/ConstraintMaker.swift | 36 +++++------ Source/ConstraintMakerRelatable.swift | 6 +- Source/ConstraintRelation.swift | 2 +- Source/ConstraintViewDSL.swift | 44 ++----------- Source/LayoutConstraint.swift | 2 +- Source/LayoutConstraintItem.swift | 92 +++++++++++++++++++++++++++ 11 files changed, 140 insertions(+), 78 deletions(-) create mode 100644 Source/LayoutConstraintItem.swift diff --git a/SnapKit.xcodeproj/project.pbxproj b/SnapKit.xcodeproj/project.pbxproj index 321270f..736d018 100644 --- a/SnapKit.xcodeproj/project.pbxproj +++ b/SnapKit.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ EE235FC31C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE235FBF1C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift */; }; EE235FC81C5785E200C08960 /* ConstraintView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE235FC61C5785E200C08960 /* ConstraintView+Extensions.swift */; }; EE4910991B19A40200A54F1F /* SnapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEBCC9D819CC627D0083B827 /* SnapKit.framework */; }; + EE6898CB1DA7B3A100D47F33 /* LayoutConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6898CA1DA7B3A100D47F33 /* LayoutConstraintItem.swift */; }; EECDB3741AC0C9B6006BBC11 /* SnapKit.h in Headers */ = {isa = PBXBuildFile; fileRef = EECDB3661AC0C95C006BBC11 /* SnapKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; EECDB3931AC0CB52006BBC11 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECDB36A1AC0C95C006BBC11 /* Tests.swift */; }; EEF68F9E1D78492400980C26 /* ConstraintLayoutGuideDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF68F9D1D78492400980C26 /* ConstraintLayoutGuideDSL.swift */; }; @@ -70,6 +71,7 @@ EE235FBE1C5785DC00C08960 /* ConstraintViewDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintViewDSL.swift; sourceTree = ""; }; EE235FBF1C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintLayoutSupportDSL.swift; sourceTree = ""; }; EE235FC61C5785E200C08960 /* ConstraintView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ConstraintView+Extensions.swift"; sourceTree = ""; }; + EE6898CA1DA7B3A100D47F33 /* LayoutConstraintItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutConstraintItem.swift; sourceTree = ""; }; EE94F6081AC0F10A008767FF /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; EE94F60A1AC0F10F008767FF /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks/AppKit.framework; sourceTree = DEVELOPER_DIR; }; EEBCC9D819CC627D0083B827 /* SnapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -184,6 +186,7 @@ EE235F6A1C5785C600C08960 /* ConstraintAttributes.swift */, EE235F6B1C5785C600C08960 /* ConstraintItem.swift */, EE235F6C1C5785C600C08960 /* LayoutConstraint.swift */, + EE6898CA1DA7B3A100D47F33 /* LayoutConstraintItem.swift */, ); name = Models; sourceTree = ""; @@ -372,6 +375,7 @@ EE235F761C5785C600C08960 /* ConstraintConfig.swift in Sources */, EE235F6D1C5785C600C08960 /* Constraint.swift in Sources */, EE235F791C5785C600C08960 /* ConstraintView.swift in Sources */, + EE6898CB1DA7B3A100D47F33 /* LayoutConstraintItem.swift in Sources */, EE235FB21C5785D400C08960 /* ConstraintMakerPriortizable.swift in Sources */, EE235F8B1C5785C600C08960 /* LayoutConstraint.swift in Sources */, EE235FA31C5785CE00C08960 /* ConstraintInsetTarget.swift in Sources */, diff --git a/Source/Constraint.swift b/Source/Constraint.swift index e5086c1..c4df105 100644 --- a/Source/Constraint.swift +++ b/Source/Constraint.swift @@ -73,7 +73,7 @@ public class Constraint { let layoutToAttributes = self.to.attributes.layoutAttributes // get layout from - let layoutFrom: ConstraintView = self.from.view! + let layoutFrom = self.from.layoutConstraintItem! // get relation let layoutRelation = self.relation.layoutRelation @@ -247,12 +247,12 @@ public class Constraint { } internal func activateIfNeeded(updatingExisting: Bool = false) { - guard let view = self.from.view else { - print("WARNING: SnapKit failed to get from view from constraint. Activate will be a no-op.") + guard let item = self.from.layoutConstraintItem else { + print("WARNING: SnapKit failed to get from item from constraint. Activate will be a no-op.") return } let layoutConstraints = self.layoutConstraints - let existingLayoutConstraints = view.snp.constraints.map({ $0.layoutConstraints }).reduce([]) { $0 + $1 } + let existingLayoutConstraints = item.constraints.map({ $0.layoutConstraints }).reduce([]) { $0 + $1 } if updatingExisting { for layoutConstraint in layoutConstraints { @@ -266,17 +266,17 @@ public class Constraint { } } else { NSLayoutConstraint.activate(layoutConstraints) - view.snp.add(constraints: [self]) + item.add(constraints: [self]) } } internal func deactivateIfNeeded() { - guard let view = self.from.view else { - print("WARNING: SnapKit failed to get from view from constraint. Deactivate will be a no-op.") + guard let item = self.from.layoutConstraintItem else { + print("WARNING: SnapKit failed to get from item from constraint. Deactivate will be a no-op.") return } let layoutConstraints = self.layoutConstraints NSLayoutConstraint.deactivate(layoutConstraints) - view.snp.remove(constraints: [self]) + item.remove(constraints: [self]) } } diff --git a/Source/ConstraintAttributes.swift b/Source/ConstraintAttributes.swift index 7236d24..4083235 100644 --- a/Source/ConstraintAttributes.swift +++ b/Source/ConstraintAttributes.swift @@ -28,7 +28,7 @@ #endif -internal struct ConstraintAttributes: OptionSet { +internal struct ConstraintAttributes : OptionSet { internal init(rawValue: UInt) { self.rawValue = rawValue diff --git a/Source/ConstraintDescription.swift b/Source/ConstraintDescription.swift index d961fb5..a6f12b7 100644 --- a/Source/ConstraintDescription.swift +++ b/Source/ConstraintDescription.swift @@ -30,7 +30,7 @@ public class ConstraintDescription { - internal let view: ConstraintView + internal let item: LayoutConstraintItem internal var attributes: ConstraintAttributes internal var relation: ConstraintRelation? = nil internal var sourceLocation: (String, UInt)? = nil @@ -45,7 +45,7 @@ public class ConstraintDescription { let sourceLocation = self.sourceLocation else { return nil } - let from = ConstraintItem(target: self.view, attributes: self.attributes) + let from = ConstraintItem(target: self.item as AnyObject, attributes: self.attributes) return Constraint( from: from, @@ -61,8 +61,8 @@ public class ConstraintDescription { // MARK: Initialization - internal init(view: ConstraintView, attributes: ConstraintAttributes) { - self.view = view + internal init(item: LayoutConstraintItem, attributes: ConstraintAttributes) { + self.item = item self.attributes = attributes } diff --git a/Source/ConstraintItem.swift b/Source/ConstraintItem.swift index a9477da..de0e175 100644 --- a/Source/ConstraintItem.swift +++ b/Source/ConstraintItem.swift @@ -28,7 +28,7 @@ #endif -public class ConstraintItem: Equatable { +public class ConstraintItem : Equatable { internal weak var target: AnyObject? internal let attributes: ConstraintAttributes @@ -38,8 +38,8 @@ public class ConstraintItem: Equatable { self.attributes = attributes } - internal var view: ConstraintView? { - return self.target as? ConstraintView + internal var layoutConstraintItem: LayoutConstraintItem? { + return self.target as? LayoutConstraintItem } } diff --git a/Source/ConstraintMaker.swift b/Source/ConstraintMaker.swift index 63c1be4..9232262 100644 --- a/Source/ConstraintMaker.swift +++ b/Source/ConstraintMaker.swift @@ -143,22 +143,22 @@ public class ConstraintMaker { return self.makeExtendableWithAttributes(.centerWithinMargins) } - private let view: ConstraintView + private let item: LayoutConstraintItem private var descriptions = [ConstraintDescription]() - internal init(view: ConstraintView) { - self.view = view - self.view.translatesAutoresizingMaskIntoConstraints = false + internal init(item: LayoutConstraintItem) { + self.item = item + self.item.prepare() } internal func makeExtendableWithAttributes(_ attributes: ConstraintAttributes) -> ConstraintMakerExtendable { - let description = ConstraintDescription(view: self.view, attributes: attributes) + let description = ConstraintDescription(item: self.item, attributes: attributes) self.descriptions.append(description) return ConstraintMakerExtendable(description) } - internal static func prepareConstraints(view: ConstraintView, closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] { - let maker = ConstraintMaker(view: view) + internal static func prepareConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] { + let maker = ConstraintMaker(item: item) closure(maker) let constraints = maker.descriptions .map { $0.constraint } @@ -167,8 +167,8 @@ public class ConstraintMaker { return constraints } - internal static func makeConstraints(view: ConstraintView, closure: (_ make: ConstraintMaker) -> Void) { - let maker = ConstraintMaker(view: view) + internal static func makeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) { + let maker = ConstraintMaker(item: item) closure(maker) let constraints = maker.descriptions .map { $0.constraint } @@ -179,18 +179,18 @@ public class ConstraintMaker { } } - internal static func remakeConstraints(view: ConstraintView, closure: (_ make: ConstraintMaker) -> Void) { - self.removeConstraints(view: view) - self.makeConstraints(view: view, closure: closure) + internal static func remakeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) { + self.removeConstraints(item: item) + self.makeConstraints(item: item, closure: closure) } - internal static func updateConstraints(view: ConstraintView, closure: (_ make: ConstraintMaker) -> Void) { - guard view.snp.constraints.count > 0 else { - self.makeConstraints(view: view, closure: closure) + internal static func updateConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) { + guard item.constraints.count > 0 else { + self.makeConstraints(item: item, closure: closure) return } - let maker = ConstraintMaker(view: view) + let maker = ConstraintMaker(item: item) closure(maker) let constraints = maker.descriptions .map { $0.constraint } @@ -201,8 +201,8 @@ public class ConstraintMaker { } } - internal static func removeConstraints(view: ConstraintView) { - let constraints = view.snp.constraints + internal static func removeConstraints(item: LayoutConstraintItem) { + let constraints = item.constraints for constraint in constraints { constraint.deactivateIfNeeded() } diff --git a/Source/ConstraintMakerRelatable.swift b/Source/ConstraintMakerRelatable.swift index d50b423..6b94e1d 100644 --- a/Source/ConstraintMakerRelatable.swift +++ b/Source/ConstraintMakerRelatable.swift @@ -76,7 +76,7 @@ public class ConstraintMakerRelatable { @discardableResult public func equalToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable { - guard let other = self.description.view.superview else { + guard let other = self.description.item.superview else { fatalError("Expected superview but found nil when attempting make constraint `equalToSuperview`.") } return self.relatedTo(other, relation: .equal, file: file, line: line) @@ -89,7 +89,7 @@ public class ConstraintMakerRelatable { @discardableResult public func lessThanOrEqualToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable { - guard let other = self.description.view.superview else { + guard let other = self.description.item.superview else { fatalError("Expected superview but found nil when attempting make constraint `lessThanOrEqualToSuperview`.") } return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line) @@ -102,7 +102,7 @@ public class ConstraintMakerRelatable { @discardableResult public func greaterThanOrEqualToSuperview(_ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable { - guard let other = self.description.view.superview else { + guard let other = self.description.item.superview else { fatalError("Expected superview but found nil when attempting make constraint `greaterThanOrEqualToSuperview`.") } return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line) diff --git a/Source/ConstraintRelation.swift b/Source/ConstraintRelation.swift index 48afbfc..d53bb3b 100644 --- a/Source/ConstraintRelation.swift +++ b/Source/ConstraintRelation.swift @@ -28,7 +28,7 @@ #endif -internal enum ConstraintRelation: Int { +internal enum ConstraintRelation : Int { case equal = 1 case lessThanOrEqual case greaterThanOrEqual diff --git a/Source/ConstraintViewDSL.swift b/Source/ConstraintViewDSL.swift index 541d18b..0242c4a 100644 --- a/Source/ConstraintViewDSL.swift +++ b/Source/ConstraintViewDSL.swift @@ -32,27 +32,25 @@ public struct ConstraintViewDSL: ConstraintAttributesDSL { @discardableResult public func prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] { - return ConstraintMaker.prepareConstraints(view: self.view, closure: closure) + return ConstraintMaker.prepareConstraints(item: self.view, closure: closure) } public func makeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { - ConstraintMaker.makeConstraints(view: self.view, closure: closure) + ConstraintMaker.makeConstraints(item: self.view, closure: closure) } public func remakeConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { - ConstraintMaker.remakeConstraints(view: self.view, closure: closure) + ConstraintMaker.remakeConstraints(item: self.view, closure: closure) } public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) { - ConstraintMaker.updateConstraints(view: self.view, closure: closure) + ConstraintMaker.updateConstraints(item: self.view, closure: closure) } public func removeConstraints() { - ConstraintMaker.removeConstraints(view: self.view) + ConstraintMaker.removeConstraints(item: self.view) } - - public var contentHuggingHorizontalPriority: Float { get { return self.view.contentHuggingPriority(for: .horizontal) @@ -100,36 +98,4 @@ public struct ConstraintViewDSL: ConstraintAttributesDSL { } - internal var constraints: [Constraint] { - return self.constraintsHashTable.allObjects - } - - internal func add(constraints: [Constraint]) { - let hashTable = self.constraintsHashTable - for constraint in constraints { - hashTable.add(constraint) - } - } - - internal func remove(constraints: [Constraint]) { - let hashTable = self.constraintsHashTable - for constraint in constraints { - hashTable.remove(constraint) - } - } - - private var constraintsHashTable: NSHashTable { - let constraints: NSHashTable - - if let existing = objc_getAssociatedObject(self.view, &constraintsKey) as? NSHashTable { - constraints = existing - } else { - constraints = NSHashTable() - objc_setAssociatedObject(self.view, &constraintsKey, constraints, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - return constraints - - } - } -private var constraintsKey: UInt8 = 0 diff --git a/Source/LayoutConstraint.swift b/Source/LayoutConstraint.swift index 27c4105..8bb5ed2 100644 --- a/Source/LayoutConstraint.swift +++ b/Source/LayoutConstraint.swift @@ -28,7 +28,7 @@ #endif -public class LayoutConstraint: NSLayoutConstraint { +public class LayoutConstraint : NSLayoutConstraint { public var label: String? { get { diff --git a/Source/LayoutConstraintItem.swift b/Source/LayoutConstraintItem.swift new file mode 100644 index 0000000..1025aba --- /dev/null +++ b/Source/LayoutConstraintItem.swift @@ -0,0 +1,92 @@ +// +// SnapKit +// +// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(iOS) || os(tvOS) + import UIKit +#else + import AppKit +#endif + + +public protocol LayoutConstraintItem: class { +} + +extension ConstraintLayoutGuide : LayoutConstraintItem { +} + +extension ConstraintView : LayoutConstraintItem { +} + + +extension LayoutConstraintItem { + + internal func prepare() { + if let view = self as? ConstraintView { + view.translatesAutoresizingMaskIntoConstraints = false + } + } + + internal var superview: ConstraintView? { + if let view = self as? ConstraintView { + return view.superview + } + + if #available(iOS 9.0, *), let guide = self as? ConstraintLayoutGuide { + return guide.owningView + } + + return nil + } + internal var constraints: [Constraint] { + return self.constraintsHashTable.allObjects + } + + internal func add(constraints: [Constraint]) { + let hashTable = self.constraintsHashTable + for constraint in constraints { + hashTable.add(constraint) + } + } + + internal func remove(constraints: [Constraint]) { + let hashTable = self.constraintsHashTable + for constraint in constraints { + hashTable.remove(constraint) + } + } + + private var constraintsHashTable: NSHashTable { + let constraints: NSHashTable + + if let existing = objc_getAssociatedObject(self, &constraintsKey) as? NSHashTable { + constraints = existing + } else { + constraints = NSHashTable() + objc_setAssociatedObject(self, &constraintsKey, constraints, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + return constraints + + } + +} +private var constraintsKey: UInt8 = 0