Begin support for LayoutGuide constraint maker

This commit is contained in:
Robert Payne 2016-10-07 23:58:56 +13:00
parent 4018d4bd12
commit dc304472aa
11 changed files with 140 additions and 78 deletions

View File

@ -33,6 +33,7 @@
EE235FC31C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE235FBF1C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift */; }; EE235FC31C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE235FBF1C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift */; };
EE235FC81C5785E200C08960 /* ConstraintView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE235FC61C5785E200C08960 /* ConstraintView+Extensions.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 */; }; 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, ); }; }; 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 */; }; EECDB3931AC0CB52006BBC11 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECDB36A1AC0C95C006BBC11 /* Tests.swift */; };
EEF68F9E1D78492400980C26 /* ConstraintLayoutGuideDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF68F9D1D78492400980C26 /* ConstraintLayoutGuideDSL.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 = "<group>"; }; EE235FBE1C5785DC00C08960 /* ConstraintViewDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintViewDSL.swift; sourceTree = "<group>"; };
EE235FBF1C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintLayoutSupportDSL.swift; sourceTree = "<group>"; }; EE235FBF1C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintLayoutSupportDSL.swift; sourceTree = "<group>"; };
EE235FC61C5785E200C08960 /* ConstraintView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ConstraintView+Extensions.swift"; sourceTree = "<group>"; }; EE235FC61C5785E200C08960 /* ConstraintView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ConstraintView+Extensions.swift"; sourceTree = "<group>"; };
EE6898CA1DA7B3A100D47F33 /* LayoutConstraintItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutConstraintItem.swift; sourceTree = "<group>"; };
EE94F6081AC0F10A008767FF /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 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; }; 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; }; EEBCC9D819CC627D0083B827 /* SnapKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnapKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -184,6 +186,7 @@
EE235F6A1C5785C600C08960 /* ConstraintAttributes.swift */, EE235F6A1C5785C600C08960 /* ConstraintAttributes.swift */,
EE235F6B1C5785C600C08960 /* ConstraintItem.swift */, EE235F6B1C5785C600C08960 /* ConstraintItem.swift */,
EE235F6C1C5785C600C08960 /* LayoutConstraint.swift */, EE235F6C1C5785C600C08960 /* LayoutConstraint.swift */,
EE6898CA1DA7B3A100D47F33 /* LayoutConstraintItem.swift */,
); );
name = Models; name = Models;
sourceTree = "<group>"; sourceTree = "<group>";
@ -372,6 +375,7 @@
EE235F761C5785C600C08960 /* ConstraintConfig.swift in Sources */, EE235F761C5785C600C08960 /* ConstraintConfig.swift in Sources */,
EE235F6D1C5785C600C08960 /* Constraint.swift in Sources */, EE235F6D1C5785C600C08960 /* Constraint.swift in Sources */,
EE235F791C5785C600C08960 /* ConstraintView.swift in Sources */, EE235F791C5785C600C08960 /* ConstraintView.swift in Sources */,
EE6898CB1DA7B3A100D47F33 /* LayoutConstraintItem.swift in Sources */,
EE235FB21C5785D400C08960 /* ConstraintMakerPriortizable.swift in Sources */, EE235FB21C5785D400C08960 /* ConstraintMakerPriortizable.swift in Sources */,
EE235F8B1C5785C600C08960 /* LayoutConstraint.swift in Sources */, EE235F8B1C5785C600C08960 /* LayoutConstraint.swift in Sources */,
EE235FA31C5785CE00C08960 /* ConstraintInsetTarget.swift in Sources */, EE235FA31C5785CE00C08960 /* ConstraintInsetTarget.swift in Sources */,

View File

@ -73,7 +73,7 @@ public class Constraint {
let layoutToAttributes = self.to.attributes.layoutAttributes let layoutToAttributes = self.to.attributes.layoutAttributes
// get layout from // get layout from
let layoutFrom: ConstraintView = self.from.view! let layoutFrom = self.from.layoutConstraintItem!
// get relation // get relation
let layoutRelation = self.relation.layoutRelation let layoutRelation = self.relation.layoutRelation
@ -247,12 +247,12 @@ public class Constraint {
} }
internal func activateIfNeeded(updatingExisting: Bool = false) { internal func activateIfNeeded(updatingExisting: Bool = false) {
guard let view = self.from.view else { guard let item = self.from.layoutConstraintItem else {
print("WARNING: SnapKit failed to get from view from constraint. Activate will be a no-op.") print("WARNING: SnapKit failed to get from item from constraint. Activate will be a no-op.")
return return
} }
let layoutConstraints = self.layoutConstraints 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 { if updatingExisting {
for layoutConstraint in layoutConstraints { for layoutConstraint in layoutConstraints {
@ -266,17 +266,17 @@ public class Constraint {
} }
} else { } else {
NSLayoutConstraint.activate(layoutConstraints) NSLayoutConstraint.activate(layoutConstraints)
view.snp.add(constraints: [self]) item.add(constraints: [self])
} }
} }
internal func deactivateIfNeeded() { internal func deactivateIfNeeded() {
guard let view = self.from.view else { guard let item = self.from.layoutConstraintItem else {
print("WARNING: SnapKit failed to get from view from constraint. Deactivate will be a no-op.") print("WARNING: SnapKit failed to get from item from constraint. Deactivate will be a no-op.")
return return
} }
let layoutConstraints = self.layoutConstraints let layoutConstraints = self.layoutConstraints
NSLayoutConstraint.deactivate(layoutConstraints) NSLayoutConstraint.deactivate(layoutConstraints)
view.snp.remove(constraints: [self]) item.remove(constraints: [self])
} }
} }

View File

@ -28,7 +28,7 @@
#endif #endif
internal struct ConstraintAttributes: OptionSet { internal struct ConstraintAttributes : OptionSet {
internal init(rawValue: UInt) { internal init(rawValue: UInt) {
self.rawValue = rawValue self.rawValue = rawValue

View File

@ -30,7 +30,7 @@
public class ConstraintDescription { public class ConstraintDescription {
internal let view: ConstraintView internal let item: LayoutConstraintItem
internal var attributes: ConstraintAttributes internal var attributes: ConstraintAttributes
internal var relation: ConstraintRelation? = nil internal var relation: ConstraintRelation? = nil
internal var sourceLocation: (String, UInt)? = nil internal var sourceLocation: (String, UInt)? = nil
@ -45,7 +45,7 @@ public class ConstraintDescription {
let sourceLocation = self.sourceLocation else { let sourceLocation = self.sourceLocation else {
return nil return nil
} }
let from = ConstraintItem(target: self.view, attributes: self.attributes) let from = ConstraintItem(target: self.item as AnyObject, attributes: self.attributes)
return Constraint( return Constraint(
from: from, from: from,
@ -61,8 +61,8 @@ public class ConstraintDescription {
// MARK: Initialization // MARK: Initialization
internal init(view: ConstraintView, attributes: ConstraintAttributes) { internal init(item: LayoutConstraintItem, attributes: ConstraintAttributes) {
self.view = view self.item = item
self.attributes = attributes self.attributes = attributes
} }

View File

@ -28,7 +28,7 @@
#endif #endif
public class ConstraintItem: Equatable { public class ConstraintItem : Equatable {
internal weak var target: AnyObject? internal weak var target: AnyObject?
internal let attributes: ConstraintAttributes internal let attributes: ConstraintAttributes
@ -38,8 +38,8 @@ public class ConstraintItem: Equatable {
self.attributes = attributes self.attributes = attributes
} }
internal var view: ConstraintView? { internal var layoutConstraintItem: LayoutConstraintItem? {
return self.target as? ConstraintView return self.target as? LayoutConstraintItem
} }
} }

View File

@ -143,22 +143,22 @@ public class ConstraintMaker {
return self.makeExtendableWithAttributes(.centerWithinMargins) return self.makeExtendableWithAttributes(.centerWithinMargins)
} }
private let view: ConstraintView private let item: LayoutConstraintItem
private var descriptions = [ConstraintDescription]() private var descriptions = [ConstraintDescription]()
internal init(view: ConstraintView) { internal init(item: LayoutConstraintItem) {
self.view = view self.item = item
self.view.translatesAutoresizingMaskIntoConstraints = false self.item.prepare()
} }
internal func makeExtendableWithAttributes(_ attributes: ConstraintAttributes) -> ConstraintMakerExtendable { 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) self.descriptions.append(description)
return ConstraintMakerExtendable(description) return ConstraintMakerExtendable(description)
} }
internal static func prepareConstraints(view: ConstraintView, closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] { internal static func prepareConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
let maker = ConstraintMaker(view: view) let maker = ConstraintMaker(item: item)
closure(maker) closure(maker)
let constraints = maker.descriptions let constraints = maker.descriptions
.map { $0.constraint } .map { $0.constraint }
@ -167,8 +167,8 @@ public class ConstraintMaker {
return constraints return constraints
} }
internal static func makeConstraints(view: ConstraintView, closure: (_ make: ConstraintMaker) -> Void) { internal static func makeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
let maker = ConstraintMaker(view: view) let maker = ConstraintMaker(item: item)
closure(maker) closure(maker)
let constraints = maker.descriptions let constraints = maker.descriptions
.map { $0.constraint } .map { $0.constraint }
@ -179,18 +179,18 @@ public class ConstraintMaker {
} }
} }
internal static func remakeConstraints(view: ConstraintView, closure: (_ make: ConstraintMaker) -> Void) { internal static func remakeConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
self.removeConstraints(view: view) self.removeConstraints(item: item)
self.makeConstraints(view: view, closure: closure) self.makeConstraints(item: item, closure: closure)
} }
internal static func updateConstraints(view: ConstraintView, closure: (_ make: ConstraintMaker) -> Void) { internal static func updateConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) {
guard view.snp.constraints.count > 0 else { guard item.constraints.count > 0 else {
self.makeConstraints(view: view, closure: closure) self.makeConstraints(item: item, closure: closure)
return return
} }
let maker = ConstraintMaker(view: view) let maker = ConstraintMaker(item: item)
closure(maker) closure(maker)
let constraints = maker.descriptions let constraints = maker.descriptions
.map { $0.constraint } .map { $0.constraint }
@ -201,8 +201,8 @@ public class ConstraintMaker {
} }
} }
internal static func removeConstraints(view: ConstraintView) { internal static func removeConstraints(item: LayoutConstraintItem) {
let constraints = view.snp.constraints let constraints = item.constraints
for constraint in constraints { for constraint in constraints {
constraint.deactivateIfNeeded() constraint.deactivateIfNeeded()
} }

View File

@ -76,7 +76,7 @@ public class ConstraintMakerRelatable {
@discardableResult @discardableResult
public func equalToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable { 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`.") fatalError("Expected superview but found nil when attempting make constraint `equalToSuperview`.")
} }
return self.relatedTo(other, relation: .equal, file: file, line: line) return self.relatedTo(other, relation: .equal, file: file, line: line)
@ -89,7 +89,7 @@ public class ConstraintMakerRelatable {
@discardableResult @discardableResult
public func lessThanOrEqualToSuperview(_ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable { 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`.") fatalError("Expected superview but found nil when attempting make constraint `lessThanOrEqualToSuperview`.")
} }
return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line) return self.relatedTo(other, relation: .lessThanOrEqual, file: file, line: line)
@ -102,7 +102,7 @@ public class ConstraintMakerRelatable {
@discardableResult @discardableResult
public func greaterThanOrEqualToSuperview(_ file: String = #file, line: UInt = #line) -> ConstraintMakerEditable { 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`.") fatalError("Expected superview but found nil when attempting make constraint `greaterThanOrEqualToSuperview`.")
} }
return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line) return self.relatedTo(other, relation: .greaterThanOrEqual, file: file, line: line)

View File

@ -28,7 +28,7 @@
#endif #endif
internal enum ConstraintRelation: Int { internal enum ConstraintRelation : Int {
case equal = 1 case equal = 1
case lessThanOrEqual case lessThanOrEqual
case greaterThanOrEqual case greaterThanOrEqual

View File

@ -32,27 +32,25 @@ public struct ConstraintViewDSL: ConstraintAttributesDSL {
@discardableResult @discardableResult
public func prepareConstraints(_ closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] { 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) { 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) { 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) { public func updateConstraints(_ closure: (_ make: ConstraintMaker) -> Void) {
ConstraintMaker.updateConstraints(view: self.view, closure: closure) ConstraintMaker.updateConstraints(item: self.view, closure: closure)
} }
public func removeConstraints() { public func removeConstraints() {
ConstraintMaker.removeConstraints(view: self.view) ConstraintMaker.removeConstraints(item: self.view)
} }
public var contentHuggingHorizontalPriority: Float { public var contentHuggingHorizontalPriority: Float {
get { get {
return self.view.contentHuggingPriority(for: .horizontal) 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<Constraint> {
let constraints: NSHashTable<Constraint>
if let existing = objc_getAssociatedObject(self.view, &constraintsKey) as? NSHashTable<Constraint> {
constraints = existing
} else {
constraints = NSHashTable<Constraint>()
objc_setAssociatedObject(self.view, &constraintsKey, constraints, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
return constraints
}
} }
private var constraintsKey: UInt8 = 0

View File

@ -28,7 +28,7 @@
#endif #endif
public class LayoutConstraint: NSLayoutConstraint { public class LayoutConstraint : NSLayoutConstraint {
public var label: String? { public var label: String? {
get { get {

View File

@ -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<Constraint> {
let constraints: NSHashTable<Constraint>
if let existing = objc_getAssociatedObject(self, &constraintsKey) as? NSHashTable<Constraint> {
constraints = existing
} else {
constraints = NSHashTable<Constraint>()
objc_setAssociatedObject(self, &constraintsKey, constraints, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
return constraints
}
}
private var constraintsKey: UInt8 = 0