added extension methods for anchoring

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2023-06-15 10:26:40 -05:00
parent d7cad33727
commit ce340fa34e

View File

@ -10,56 +10,203 @@ import UIKit
import VDSFormControlsTokens
extension UIView {
public func pin(_ view: UIView, with edges: UIEdgeInsets = UIEdgeInsets.zero) {
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: edges.left).isActive = true
trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -edges.right).isActive = true
topAnchor.constraint(equalTo: view.topAnchor, constant: edges.top).isActive = true
bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -edges.bottom).isActive = true
public enum ConstraintIdentifier: CustomStringConvertible {
case leading, trailing, top, bottom, height, width
case custom(String)
public var description: String {
switch self {
case .leading: return "leading"
case .trailing: return "trailing"
case .top: return "top"
case .bottom: return "bottom"
case .height: return "height"
case .width: return "width"
case .custom(let identifier): return identifier
}
}
}
public enum ConstraintType {
case equal, lessThanOrEqual, greaterThanOrEqual
}
public var _topConstraint: NSLayoutConstraint? { constraint(with: .top)}
public var _leadingConstraint: NSLayoutConstraint? { constraint(with: .leading)}
public var _trailingConstraint: NSLayoutConstraint? { constraint(with: .trailing)}
public var _bottomConstraint: NSLayoutConstraint? { constraint(with: .bottom)}
public var _widthConstraint: NSLayoutConstraint? { constraint(with: .width)}
public var _heightConstraint: NSLayoutConstraint? { constraint(with: .height)}
public func constraint(with identifier: ConstraintIdentifier) -> NSLayoutConstraint? {
return constraint(with: identifier.description)
}
public func constraint(with identifier: String) -> NSLayoutConstraint? {
return constraints.first { $0.identifier == identifier }
}
public func removeConstraint(edges: [UIRectEdge]) {
let topConstraint: NSLayoutConstraint? = constraint(with: .top)
let leadingConstraint: NSLayoutConstraint? = constraint(with: .leading)
let trailingConstraint: NSLayoutConstraint? = constraint(with: .trailing)
let bottomConstraint: NSLayoutConstraint? = constraint(with: .bottom)
edges.forEach { edge in
switch edge {
case .all:
if let leadingConstraint {
removeConstraint(leadingConstraint)
}
if let trailingConstraint {
removeConstraint(trailingConstraint)
}
if let topConstraint {
removeConstraint(topConstraint)
}
if let bottomConstraint {
removeConstraint(bottomConstraint)
}
case .left:
if let leadingConstraint {
removeConstraint(leadingConstraint)
}
case .right:
if let trailingConstraint {
removeConstraint(trailingConstraint)
}
case .top:
if let topConstraint {
removeConstraint(topConstraint)
}
case .bottom:
if let bottomConstraint {
removeConstraint(bottomConstraint)
}
default:
break
}
}
}
}
//--------------------------------------------------
// MARK: - Pinning
//--------------------------------------------------
extension UIView {
public func pin(_ view: UIView, with edges: UIEdgeInsets = UIEdgeInsets.zero) {
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: edges.left, identifier: ConstraintIdentifier.leading.description).isActive = true
trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -edges.right, identifier: ConstraintIdentifier.trailing.description).isActive = true
topAnchor.constraint(equalTo: view.topAnchor, constant: edges.top, identifier: ConstraintIdentifier.top.description).isActive = true
bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -edges.bottom, identifier: ConstraintIdentifier.bottom.description).isActive = true
}
public func pinToSuperView(_ edges: UIEdgeInsets = UIEdgeInsets.zero) {
if let superview {
pin(superview, with: edges)
}
}
}
//--------------------------------------------------
// MARK: - HeightAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
public func height(_ constant: CGFloat, type: ConstraintType = .equal) -> Self {
switch type {
case .equal:
height(constant)
case .lessThanOrEqual:
heightLessThanEqualTo(constant)
case .greaterThanOrEqual:
heightGreaterThanEqualTo(constant)
}
return self
}
@discardableResult
public func height(_ constant: CGFloat) -> Self {
heightAnchor.constraint(equalToConstant: constant).isActive = true
heightAnchor.constraint(equalToConstant: constant, identifier: ConstraintIdentifier.height.description).isActive = true
return self
}
@discardableResult
public func heightGreaterThanEqual(_ constant: CGFloat) -> Self {
heightAnchor.constraint(greaterThanOrEqualToConstant: constant).isActive = true
return self
}
@discardableResult
public func heightLessThanEqual(_ constant: CGFloat) -> Self {
heightAnchor.constraint(lessThanOrEqualToConstant: constant).isActive = true
return self
}
@discardableResult
public func width(_ constant: CGFloat) -> Self {
widthAnchor.constraint(equalToConstant: constant).isActive = true
return self
}
@discardableResult
public func widthGreaterThanEqual(_ constant: CGFloat) -> Self {
widthAnchor.constraint(greaterThanOrEqualToConstant: constant).isActive = true
public func heightGreaterThanEqualTo(_ constant: CGFloat) -> Self {
heightAnchor.constraint(greaterThanOrEqualToConstant: constant, identifier: ConstraintIdentifier.height.description).isActive = true
return self
}
@discardableResult
public func widthLessThanEqual(_ constant: CGFloat) -> Self {
widthAnchor.constraint(lessThanOrEqualToConstant: constant).isActive = true
public func heightLessThanEqualTo(_ constant: CGFloat) -> Self {
heightAnchor.constraint(lessThanOrEqualToConstant: constant, identifier: ConstraintIdentifier.height.description).isActive = true
return self
}
}
//--------------------------------------------------
// MARK: - WidthAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
public func width(_ constant: CGFloat, type: ConstraintType = .equal) -> Self {
switch type {
case .equal:
width(constant)
case .lessThanOrEqual:
widthLessThanEqualTo(constant)
case .greaterThanOrEqual:
widthGreaterThanEqualTo(constant)
}
return self
}
@discardableResult
public func width(_ constant: CGFloat) -> Self {
widthAnchor.constraint(equalToConstant: constant, identifier: ConstraintIdentifier.width.description).isActive = true
return self
}
@discardableResult
public func widthGreaterThanEqualTo(_ constant: CGFloat) -> Self {
widthAnchor.constraint(greaterThanOrEqualToConstant: constant, identifier: ConstraintIdentifier.width.description).isActive = true
return self
}
@discardableResult
public func widthLessThanEqualTo(_ constant: CGFloat) -> Self {
widthAnchor.constraint(lessThanOrEqualToConstant: constant, identifier: ConstraintIdentifier.width.description).isActive = true
return self
}
}
//--------------------------------------------------
// MARK: - TopAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
public func pinTop(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ type: ConstraintType = .equal) -> Self {
switch type {
case .equal:
pinTop(anchor, constant)
case .lessThanOrEqual:
pinTopLessThanOrEqualTo(anchor, constant)
case .greaterThanOrEqual:
pinTopGreaterThanOrEqualTo(anchor, constant)
}
return self
}
@discardableResult
public func pinTop(_ constant: CGFloat = 0.0) -> Self {
return pinTop(nil, constant)
@ -69,11 +216,49 @@ extension UIView {
public func pinTop(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.topAnchor
if let found {
topAnchor.constraint(equalTo: found, constant: constant).isActive = true
topAnchor.constraint(equalTo: found, constant: constant, identifier: ConstraintIdentifier.top.description).isActive = true
}
return self
}
@discardableResult
public func pinTopLessThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.topAnchor
if let found {
topAnchor.constraint(lessThanOrEqualTo: found, constant: constant, identifier: ConstraintIdentifier.top.description).isActive = true
}
return self
}
@discardableResult
public func pinTopGreaterThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.topAnchor
if let found {
topAnchor.constraint(greaterThanOrEqualTo: found, constant: constant, identifier: ConstraintIdentifier.top.description).isActive = true
}
return self
}
}
//--------------------------------------------------
// MARK: - BottomAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
public func pinBottom(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ type: ConstraintType = .equal) -> Self {
switch type {
case .equal:
pinBottom(anchor, constant)
case .lessThanOrEqual:
pinBottomLessThanOrEqualTo(anchor, constant)
case .greaterThanOrEqual:
pinBottomGreaterThanOrEqualTo(anchor, constant)
}
return self
}
@discardableResult
public func pinBottom(_ constant: CGFloat = 0.0) -> Self {
return pinBottom(nil, constant)
@ -83,7 +268,45 @@ extension UIView {
public func pinBottom(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.bottomAnchor
if let found {
bottomAnchor.constraint(equalTo: found, constant: -constant).isActive = true
bottomAnchor.constraint(equalTo: found, constant: -constant, identifier: ConstraintIdentifier.bottom.description).isActive = true
}
return self
}
@discardableResult
public func pinBottomLessThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.bottomAnchor
if let found {
bottomAnchor.constraint(lessThanOrEqualTo: found, constant: -constant, identifier: ConstraintIdentifier.bottom.description).isActive = true
}
return self
}
@discardableResult
public func pinBottomGreaterThanOrEqualTo(_ anchor: NSLayoutYAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutYAxisAnchor? = anchor ?? superview?.bottomAnchor
if let found {
bottomAnchor.constraint(greaterThanOrEqualTo: found, constant: -constant, identifier: ConstraintIdentifier.bottom.description).isActive = true
}
return self
}
}
//--------------------------------------------------
// MARK: - LeadingAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
public func pinLeading(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ type: ConstraintType = .equal) -> Self {
switch type {
case .equal:
pinLeading(anchor, constant)
case .lessThanOrEqual:
pinLeadingLessThanOrEqualTo(anchor, constant)
case .greaterThanOrEqual:
pinLeadingGreaterThanOrEqualTo(anchor, constant)
}
return self
}
@ -92,16 +315,55 @@ extension UIView {
public func pinLeading(_ constant: CGFloat = 0.0) -> Self {
return pinLeading(nil, constant)
}
@discardableResult
public func pinLeading(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.leadingAnchor
if let found {
leadingAnchor.constraint(equalTo: found, constant: constant).isActive = true
leadingAnchor.constraint(equalTo: found, constant: constant, identifier: ConstraintIdentifier.leading.description).isActive = true
}
return self
}
@discardableResult
public func pinLeadingLessThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.leadingAnchor
if let found {
leadingAnchor.constraint(lessThanOrEqualTo: found, constant: constant, identifier: ConstraintIdentifier.leading.description).isActive = true
}
return self
}
@discardableResult
public func pinLeadingGreaterThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.leadingAnchor
if let found {
leadingAnchor.constraint(greaterThanOrEqualTo: found, constant: constant, identifier: ConstraintIdentifier.leading.description).isActive = true
}
return self
}
}
//--------------------------------------------------
// MARK: - TrailingAnchor
//--------------------------------------------------
extension UIView {
@discardableResult
public func pinTrailing(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0, _ type: ConstraintType = .equal) -> Self {
switch type {
case .equal:
pinTrailing(anchor, constant)
case .lessThanOrEqual:
pinTrailingLessThanOrEqualTo(anchor, constant)
case .greaterThanOrEqual:
pinLeadingGreaterThanOrEqualTo(anchor, constant)
}
return self
}
@discardableResult
public func pinTrailing(_ constant: CGFloat = 0.0) -> Self {
return pinTrailing(nil, constant)
@ -111,13 +373,33 @@ extension UIView {
public func pinTrailing(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.trailingAnchor
if let found {
trailingAnchor.constraint(equalTo: found, constant: -constant).isActive = true
trailingAnchor.constraint(equalTo: found, constant: -constant, identifier: ConstraintIdentifier.trailing.description).isActive = true
}
return self
}
@discardableResult
public func pinTrailingLessThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.trailingAnchor
if let found {
trailingAnchor.constraint(lessThanOrEqualTo: found, constant: -constant, identifier: ConstraintIdentifier.trailing.description).isActive = true
}
return self
}
@discardableResult
public func pinTrailingGreaterThanOrEqualTo(_ anchor: NSLayoutXAxisAnchor? = nil, _ constant: CGFloat = 0.0) -> Self {
let found: NSLayoutXAxisAnchor? = anchor ?? superview?.trailingAnchor
if let found {
trailingAnchor.constraint(greaterThanOrEqualTo: found, constant: -constant, identifier: ConstraintIdentifier.trailing.description).isActive = true
}
return self
}
}
//--------------------------------------------------
// MARK: - Debug Borders
//--------------------------------------------------
extension UIView {
internal func removeDebugBorder() {
@ -174,6 +456,9 @@ extension UIView {
}
}
//--------------------------------------------------
// MARK: - CALayer
//--------------------------------------------------
extension CALayer {
func remove(layerName: String) {
guard let sublayers = sublayers else {
@ -188,7 +473,9 @@ extension CALayer {
}
}
//--------------------------------------------------
// MARK: - Borders
//--------------------------------------------------
extension UIView {
public func addBorder(side: UIRectEdge, width: CGFloat, color: UIColor, offset: CGFloat = 0) {
@ -239,3 +526,126 @@ extension UIView {
}
}
}
//--------------------------------------------------
// 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
}
}