diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 10ade0c0..d5893104 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -106,6 +106,9 @@ EAC925842911C63100091998 /* Colorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEDF28F49DB3003B3210 /* Colorable.swift */; }; EAC9258C2911C9DE00091998 /* InputField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925872911C9DE00091998 /* InputField.swift */; }; EAC9258F2911C9DE00091998 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9258B2911C9DE00091998 /* EntryField.swift */; }; + EAD062A72A3B67770015965D /* UIView+CALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD062A62A3B67770015965D /* UIView+CALayer.swift */; }; + EAD062A92A3B67B10015965D /* NSLayoutAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD062A82A3B67B10015965D /* NSLayoutAnchor.swift */; }; + EAD062AB2A3B67D00015965D /* NSLayoutDimension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD062AA2A3B67D00015965D /* NSLayoutDimension.swift */; }; EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; EAF1FE9929D4850E00101452 /* Clickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9829D4850E00101452 /* Clickable.swift */; }; EAF1FE9B29DB1A6000101452 /* Changeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF1FE9A29DB1A6000101452 /* Changeable.swift */; }; @@ -237,6 +240,9 @@ EAC925822911B35300091998 /* TextLinkCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLinkCaret.swift; sourceTree = ""; }; EAC925872911C9DE00091998 /* InputField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputField.swift; sourceTree = ""; }; EAC9258B2911C9DE00091998 /* EntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = ""; }; + EAD062A62A3B67770015965D /* UIView+CALayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+CALayer.swift"; sourceTree = ""; }; + EAD062A82A3B67B10015965D /* NSLayoutAnchor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLayoutAnchor.swift; sourceTree = ""; }; + EAD062AA2A3B67D00015965D /* NSLayoutDimension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLayoutDimension.swift; sourceTree = ""; }; EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = ""; }; EAF1FE9829D4850E00101452 /* Clickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clickable.swift; sourceTree = ""; }; EAF1FE9A29DB1A6000101452 /* Changeable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Changeable.swift; sourceTree = ""; }; @@ -429,12 +435,15 @@ EAF7F0992899B17200B287F5 /* CATransaction.swift */, EA33622D2891EA3C0071C351 /* DispatchQueue+Once.swift */, EABFEB632A26473700C4C106 /* NSAttributedString.swift */, + EAD062A82A3B67B10015965D /* NSLayoutAnchor.swift */, + EAD062AA2A3B67D00015965D /* NSLayoutDimension.swift */, EAB2376529E9952D00AABE9A /* UIApplication.swift */, EA3361A7288B23300071C351 /* UIColor.swift */, EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */, EA33623D2892EE950071C351 /* UIDevice.swift */, EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */, EAB5FED329267EB300998C17 /* UIView.swift */, + EAD062A62A3B67770015965D /* UIView+CALayer.swift */, EAB5FF0029424ACB00998C17 /* UIControl.swift */, EA985C662970C21600F2FF2E /* VDSLayout.swift */, ); @@ -884,6 +893,7 @@ EA33624728931B050071C351 /* Initable.swift in Sources */, EAF7F0A4289B017C00B287F5 /* LabelAttributeModel.swift in Sources */, EA5F86D02A1F936100BC83E4 /* TabsContainer.swift in Sources */, + EAD062A92A3B67B10015965D /* NSLayoutAnchor.swift in Sources */, EAF7F0B1289B177F00B287F5 /* ColorLabelAttribute.swift in Sources */, EAC9258F2911C9DE00091998 /* EntryField.swift in Sources */, EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */, @@ -927,6 +937,7 @@ EAB2376629E9952D00AABE9A /* UIApplication.swift in Sources */, EAB5FED429267EB300998C17 /* UIView.swift in Sources */, EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */, + EAD062AB2A3B67D00015965D /* NSLayoutDimension.swift in Sources */, EA33623E2892EE950071C351 /* UIDevice.swift in Sources */, EA985C692971B90B00F2FF2E /* IconSize.swift in Sources */, EA985C672970C21600F2FF2E /* VDSLayout.swift in Sources */, @@ -941,6 +952,7 @@ EA1F266628B945070033E859 /* RadioSwatchGroup.swift in Sources */, EA596ABF2A16B4F500300C4B /* Tabs.swift in Sources */, EAC71A212A2E1DC000E47A9F /* SelectorItemBase.swift in Sources */, + EAD062A72A3B67770015965D /* UIView+CALayer.swift in Sources */, EA985BEC2968A91200F2FF2E /* TitleLockupTitleModel.swift in Sources */, 5FC35BE328D51405004EBEAC /* Button.swift in Sources */, ); diff --git a/VDS/Extensions/NSLayoutAnchor.swift b/VDS/Extensions/NSLayoutAnchor.swift new file mode 100644 index 00000000..21c526e6 --- /dev/null +++ b/VDS/Extensions/NSLayoutAnchor.swift @@ -0,0 +1,58 @@ +// +// NSLayoutAnchor.swift +// VDS +// +// Created by Matt Bruce on 6/15/23. +// + +import Foundation +import UIKit + +//-------------------------------------------------- +// MARK: - NSLayoutAnchor +//-------------------------------------------------- +extension NSLayoutAnchor { + // These methods return an inactive constraint of the form thisAnchor = otherAnchor. + @discardableResult + @objc public func constraint(equalTo anchor: NSLayoutAnchor, identifier: String) -> NSLayoutConstraint { + let constraint = self.constraint(equalTo: anchor) + constraint.identifier = identifier + return constraint + } + + @discardableResult + @objc public func constraint(greaterThanOrEqualTo anchor: NSLayoutAnchor, identifier: String) -> NSLayoutConstraint { + let constraint = self.constraint(greaterThanOrEqualTo: anchor) + constraint.identifier = identifier + return constraint + } + + @discardableResult + @objc public func constraint(lessThanOrEqualTo anchor: NSLayoutAnchor, identifier: String) -> NSLayoutConstraint { + let constraint = self.constraint(lessThanOrEqualTo: anchor) + constraint.identifier = identifier + return constraint + } + + + @discardableResult + @objc public func constraint(equalTo anchor: NSLayoutAnchor, constant: CGFloat, identifier: String) -> NSLayoutConstraint { + let constraint = self.constraint(equalTo: anchor, constant: constant) + constraint.identifier = identifier + return constraint + } + + @discardableResult + @objc public func constraint(greaterThanOrEqualTo anchor: NSLayoutAnchor, constant: CGFloat, identifier: String) -> NSLayoutConstraint { + let constraint = self.constraint(greaterThanOrEqualTo: anchor, constant: constant) + constraint.identifier = identifier + return constraint + } + + @discardableResult + @objc public func constraint(lessThanOrEqualTo anchor: NSLayoutAnchor, constant: CGFloat, identifier: String) -> NSLayoutConstraint { + let constraint = self.constraint(lessThanOrEqualTo: anchor, constant: constant) + constraint.identifier = identifier + return constraint + } +} diff --git a/VDS/Extensions/NSLayoutDimension.swift b/VDS/Extensions/NSLayoutDimension.swift new file mode 100644 index 00000000..d8eacf79 --- /dev/null +++ b/VDS/Extensions/NSLayoutDimension.swift @@ -0,0 +1,84 @@ +// +// NSLayoutDimension.swift +// VDS +// +// Created by Matt Bruce on 6/15/23. +// + +import Foundation +import UIKit + +//-------------------------------------------------- +// MARK: - NSLayoutDimension +//-------------------------------------------------- +extension NSLayoutDimension { + // These methods return an inactive constraint of the form thisVariable = constant. + @discardableResult + @objc public func constraint(equalToConstant c: CGFloat, identifier: String) -> NSLayoutConstraint { + let lc = constraint(equalToConstant: c) + lc.identifier = identifier + return lc + } + + @discardableResult + @objc public func constraint(greaterThanOrEqualToConstant c: CGFloat, identifier: String) -> NSLayoutConstraint { + let lc = constraint(greaterThanOrEqualToConstant: c) + lc.identifier = identifier + return lc + } + + @discardableResult + @objc public func constraint(lessThanOrEqualToConstant c: CGFloat, identifier: String) -> NSLayoutConstraint { + let lc = constraint(lessThanOrEqualToConstant: c) + lc.identifier = identifier + return lc + } + + + // These methods return an inactive constraint of the form thisAnchor = otherAnchor * multiplier. + @discardableResult + @objc public func constraint(equalTo anchor: NSLayoutDimension, multiplier m: CGFloat, identifier: String) -> NSLayoutConstraint { + let lc = constraint(equalTo: anchor, multiplier: m) + lc.identifier = identifier + return lc + } + + @discardableResult + @objc public func constraint(greaterThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, identifier: String) -> NSLayoutConstraint { + let lc = constraint(greaterThanOrEqualTo: anchor ,multiplier: m) + lc.identifier = identifier + return lc + } + + @discardableResult + @objc public func constraint(lessThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, identifier: String) -> NSLayoutConstraint { + let lc = constraint(lessThanOrEqualTo: anchor, multiplier: m) + lc.identifier = identifier + return lc + } + + + // These methods return an inactive constraint of the form thisAnchor = otherAnchor * multiplier + constant. + @discardableResult + @objc public func constraint(equalTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat, identifier: String) -> NSLayoutConstraint { + let lc = constraint(equalTo: anchor, multiplier: m, constant: c) + lc.identifier = identifier + return lc + } + + @discardableResult + @objc public func constraint(greaterThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat, identifier: String) -> NSLayoutConstraint { + let lc = constraint(greaterThanOrEqualTo: anchor, multiplier: m, constant: c) + lc.identifier = identifier + return lc + } + + @discardableResult + @objc public func constraint(lessThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat, identifier: String) -> NSLayoutConstraint { + let lc = constraint(lessThanOrEqualTo: anchor, multiplier: m, constant: c) + lc.identifier = identifier + return lc + } + +} + diff --git a/VDS/Extensions/UIView+CALayer.swift b/VDS/Extensions/UIView+CALayer.swift new file mode 100644 index 00000000..2ad15363 --- /dev/null +++ b/VDS/Extensions/UIView+CALayer.swift @@ -0,0 +1,140 @@ +// +// UIView+CALayer.swift +// VDS +// +// Created by Matt Bruce on 6/15/23. +// + +import Foundation +import UIKit +import VDSFormControlsTokens + +//-------------------------------------------------- +// MARK: - Debug Borders +//-------------------------------------------------- +extension UIView { + + internal func removeDebugBorder() { + layer.remove(layerName: "debug") + } + + internal func addDebugBorder(color: UIColor = .red) { + //ensure you remove existing + removeDebugBorder() + + //add bounds border + let borderLayer = CALayer() + borderLayer.name = "debugAreaLayer" + borderLayer.frame = bounds + borderLayer.bounds = bounds + borderLayer.borderWidth = VDSFormControls.widthBorder + borderLayer.borderColor = color.cgColor + layer.addSublayer(borderLayer) + + //add touchborder if applicable + if type(of: self) is AppleGuidlinesTouchable.Type { + let faultToleranceX: CGFloat = max((45 - bounds.size.width) / 2.0, 0) + let faultToleranceY: CGFloat = max((45 - bounds.size.height) / 2.0, 0) + + let touchableAreaPath = UIBezierPath(rect: bounds.insetBy(dx: -faultToleranceX, dy: -faultToleranceY)) + let touchLayer = CAShapeLayer() + touchLayer.path = touchableAreaPath.cgPath + touchLayer.strokeColor = color.cgColor + touchLayer.fillColor = UIColor.clear.cgColor + touchLayer.lineWidth = VDSFormControls.widthBorder + touchLayer.opacity = 1.0 + touchLayer.name = "debugTouchableAreaLayer" + touchLayer.zPosition = 100 + touchLayer.frame = bounds + touchLayer.bounds = bounds + layer.addSublayer(touchLayer) + } + } + + public var hasDebugBorder: Bool { + guard let layers = layer.sublayers else { return false } + return layers.compactMap{$0.name}.filter{$0.hasPrefix("debug")}.count > 0 + } + + public func debugBorder(show shouldShow: Bool = true, color: UIColor = .red) { + if shouldShow { + addDebugBorder(color: color) + } else { + removeDebugBorder() + } + if let view = self as? Handlerable { + view.updateView() + } + } +} + +//-------------------------------------------------- +// MARK: - CALayer +//-------------------------------------------------- +extension CALayer { + func remove(layerName: String) { + guard let sublayers = sublayers else { + return + } + + sublayers.forEach({ layer in + if layer.name?.hasPrefix(layerName) ?? false { + layer.removeFromSuperlayer() + } + }) + } +} + +//-------------------------------------------------- +// MARK: - Borders +//-------------------------------------------------- +extension UIView { + + public func addBorder(side: UIRectEdge, width: CGFloat, color: UIColor, offset: CGFloat = 0) { + let layerName = borderLayerName(for: side) + layer.remove(layerName: layerName) + + let borderLayer = CALayer() + borderLayer.backgroundColor = color.cgColor + borderLayer.name = layerName + + switch side { + case .left: + borderLayer.frame = CGRect(x: 0, y: 0, width: width, height: frame.height) + case .right: + borderLayer.frame = CGRect(x: frame.width - width - offset, y: 0, width: width, height: frame.height) + case .top: + borderLayer.frame = CGRect(x: 0, y: 0, width: frame.width, height: width) + case .bottom: + borderLayer.frame = CGRect(x: 0, y: frame.height - width - offset, width: frame.width, height: width) + default: + break + } + + layer.addSublayer(borderLayer) + } + + public func removeBorders() { + layer.borderWidth = 0 + layer.borderColor = nil + layer.remove(layerName: borderLayerName(for: .top)) + layer.remove(layerName: borderLayerName(for: .left)) + layer.remove(layerName: borderLayerName(for: .right)) + layer.remove(layerName: borderLayerName(for: .bottom)) + } + + private func borderLayerName(for side: UIRectEdge) -> String { + switch side { + case .left: + return "leftBorderLayer" + case .right: + return "rightBorderLayer" + case .top: + return "topBorderLayer" + case .bottom: + return "bottomBorderLayer" + default: + return "" + } + } +} diff --git a/VDS/Extensions/UIView.swift b/VDS/Extensions/UIView.swift index 792834ef..1196e5e6 100644 --- a/VDS/Extensions/UIView.swift +++ b/VDS/Extensions/UIView.swift @@ -396,256 +396,3 @@ extension UIView { return self } } - -//-------------------------------------------------- -// MARK: - Debug Borders -//-------------------------------------------------- -extension UIView { - - internal func removeDebugBorder() { - layer.remove(layerName: "debug") - } - - internal func addDebugBorder(color: UIColor = .red) { - //ensure you remove existing - removeDebugBorder() - - //add bounds border - let borderLayer = CALayer() - borderLayer.name = "debugAreaLayer" - borderLayer.frame = bounds - borderLayer.bounds = bounds - borderLayer.borderWidth = VDSFormControls.widthBorder - borderLayer.borderColor = color.cgColor - layer.addSublayer(borderLayer) - - //add touchborder if applicable - if type(of: self) is AppleGuidlinesTouchable.Type { - let faultToleranceX: CGFloat = max((45 - bounds.size.width) / 2.0, 0) - let faultToleranceY: CGFloat = max((45 - bounds.size.height) / 2.0, 0) - - let touchableAreaPath = UIBezierPath(rect: bounds.insetBy(dx: -faultToleranceX, dy: -faultToleranceY)) - let touchLayer = CAShapeLayer() - touchLayer.path = touchableAreaPath.cgPath - touchLayer.strokeColor = color.cgColor - touchLayer.fillColor = UIColor.clear.cgColor - touchLayer.lineWidth = VDSFormControls.widthBorder - touchLayer.opacity = 1.0 - touchLayer.name = "debugTouchableAreaLayer" - touchLayer.zPosition = 100 - touchLayer.frame = bounds - touchLayer.bounds = bounds - layer.addSublayer(touchLayer) - } - } - - public var hasDebugBorder: Bool { - guard let layers = layer.sublayers else { return false } - return layers.compactMap{$0.name}.filter{$0.hasPrefix("debug")}.count > 0 - } - - public func debugBorder(show shouldShow: Bool = true, color: UIColor = .red) { - if shouldShow { - addDebugBorder(color: color) - } else { - removeDebugBorder() - } - if let view = self as? Handlerable { - view.updateView() - } - } -} - -//-------------------------------------------------- -// MARK: - CALayer -//-------------------------------------------------- -extension CALayer { - func remove(layerName: String) { - guard let sublayers = sublayers else { - return - } - - sublayers.forEach({ layer in - if layer.name?.hasPrefix(layerName) ?? false { - layer.removeFromSuperlayer() - } - }) - } -} - -//-------------------------------------------------- -// MARK: - Borders -//-------------------------------------------------- -extension UIView { - - public func addBorder(side: UIRectEdge, width: CGFloat, color: UIColor, offset: CGFloat = 0) { - let layerName = borderLayerName(for: side) - layer.remove(layerName: layerName) - - let borderLayer = CALayer() - borderLayer.backgroundColor = color.cgColor - borderLayer.name = layerName - - switch side { - case .left: - borderLayer.frame = CGRect(x: 0, y: 0, width: width, height: frame.height) - case .right: - borderLayer.frame = CGRect(x: frame.width - width - offset, y: 0, width: width, height: frame.height) - case .top: - borderLayer.frame = CGRect(x: 0, y: 0, width: frame.width, height: width) - case .bottom: - borderLayer.frame = CGRect(x: 0, y: frame.height - width - offset, width: frame.width, height: width) - default: - break - } - - layer.addSublayer(borderLayer) - } - - public func removeBorders() { - layer.borderWidth = 0 - layer.borderColor = nil - layer.remove(layerName: borderLayerName(for: .top)) - layer.remove(layerName: borderLayerName(for: .left)) - layer.remove(layerName: borderLayerName(for: .right)) - layer.remove(layerName: borderLayerName(for: .bottom)) - } - - private func borderLayerName(for side: UIRectEdge) -> String { - switch side { - case .left: - return "leftBorderLayer" - case .right: - return "rightBorderLayer" - case .top: - return "topBorderLayer" - case .bottom: - return "bottomBorderLayer" - default: - return "" - } - } -} - -//-------------------------------------------------- -// MARK: - NSLayoutAnchor -//-------------------------------------------------- -extension NSLayoutAnchor { - // These methods return an inactive constraint of the form thisAnchor = otherAnchor. - @discardableResult - @objc public func constraint(equalTo anchor: NSLayoutAnchor, identifier: String) -> NSLayoutConstraint { - let constraint = self.constraint(equalTo: anchor) - constraint.identifier = identifier - return constraint - } - - @discardableResult - @objc public func constraint(greaterThanOrEqualTo anchor: NSLayoutAnchor, identifier: String) -> NSLayoutConstraint { - let constraint = self.constraint(greaterThanOrEqualTo: anchor) - constraint.identifier = identifier - return constraint - } - - @discardableResult - @objc public func constraint(lessThanOrEqualTo anchor: NSLayoutAnchor, identifier: String) -> NSLayoutConstraint { - let constraint = self.constraint(lessThanOrEqualTo: anchor) - constraint.identifier = identifier - return constraint - } - - - @discardableResult - @objc public func constraint(equalTo anchor: NSLayoutAnchor, constant: CGFloat, identifier: String) -> NSLayoutConstraint { - let constraint = self.constraint(equalTo: anchor, constant: constant) - constraint.identifier = identifier - return constraint - } - - @discardableResult - @objc public func constraint(greaterThanOrEqualTo anchor: NSLayoutAnchor, constant: CGFloat, identifier: String) -> NSLayoutConstraint { - let constraint = self.constraint(greaterThanOrEqualTo: anchor, constant: constant) - constraint.identifier = identifier - return constraint - } - - @discardableResult - @objc public func constraint(lessThanOrEqualTo anchor: NSLayoutAnchor, constant: CGFloat, identifier: String) -> NSLayoutConstraint { - let constraint = self.constraint(lessThanOrEqualTo: anchor, constant: constant) - constraint.identifier = identifier - return constraint - } -} - -//-------------------------------------------------- -// MARK: - NSLayoutDimension -//-------------------------------------------------- -extension NSLayoutDimension { - // These methods return an inactive constraint of the form thisVariable = constant. - @discardableResult - @objc public func constraint(equalToConstant c: CGFloat, identifier: String) -> NSLayoutConstraint { - let lc = constraint(equalToConstant: c) - lc.identifier = identifier - return lc - } - - @discardableResult - @objc public func constraint(greaterThanOrEqualToConstant c: CGFloat, identifier: String) -> NSLayoutConstraint { - let lc = constraint(greaterThanOrEqualToConstant: c) - lc.identifier = identifier - return lc - } - - @discardableResult - @objc public func constraint(lessThanOrEqualToConstant c: CGFloat, identifier: String) -> NSLayoutConstraint { - let lc = constraint(lessThanOrEqualToConstant: c) - lc.identifier = identifier - return lc - } - - - // These methods return an inactive constraint of the form thisAnchor = otherAnchor * multiplier. - @discardableResult - @objc public func constraint(equalTo anchor: NSLayoutDimension, multiplier m: CGFloat, identifier: String) -> NSLayoutConstraint { - let lc = constraint(equalTo: anchor, multiplier: m) - lc.identifier = identifier - return lc - } - - @discardableResult - @objc public func constraint(greaterThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, identifier: String) -> NSLayoutConstraint { - let lc = constraint(greaterThanOrEqualTo: anchor ,multiplier: m) - lc.identifier = identifier - return lc - } - - @discardableResult - @objc public func constraint(lessThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, identifier: String) -> NSLayoutConstraint { - let lc = constraint(lessThanOrEqualTo: anchor, multiplier: m) - lc.identifier = identifier - return lc - } - - - // These methods return an inactive constraint of the form thisAnchor = otherAnchor * multiplier + constant. - @discardableResult - @objc public func constraint(equalTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat, identifier: String) -> NSLayoutConstraint { - let lc = constraint(equalTo: anchor, multiplier: m, constant: c) - lc.identifier = identifier - return lc - } - - @discardableResult - @objc public func constraint(greaterThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat, identifier: String) -> NSLayoutConstraint { - let lc = constraint(greaterThanOrEqualTo: anchor, multiplier: m, constant: c) - lc.identifier = identifier - return lc - } - - @discardableResult - @objc public func constraint(lessThanOrEqualTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat, identifier: String) -> NSLayoutConstraint { - let lc = constraint(lessThanOrEqualTo: anchor, multiplier: m, constant: c) - lc.identifier = identifier - return lc - } - -}