Last active
July 5, 2023 13:58
-
-
Save ATahhan/9c8e65d9c4bace74c33ecfb4038d1958 to your computer and use it in GitHub Desktop.
Multiple flavors of advancedAnchor functions to make coding constraints faster and more enjoyable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// UIView+AdvancedAnchor.swift | |
// | |
// Created by Ammar Altahhan on 06/08/2019. | |
// Copyright © 2019 Ammar Altahhan. All rights reserved. | |
// | |
// Github: https://github.com/ATahhan | |
// Twitter: https://twitter.com/atahhan_ | |
// | |
/* | |
Examples: | |
UIView().advancedAnchor( | |
.topTo(topAnchor, 12), | |
.widthTo(widthAnchor, .multiplier(.greaterThan(0.95))), | |
.height(.constant(.lessThan(100))), | |
.centerXTo(centerXAnchor) | |
) | |
UIView().advancedAnchor( | |
top: topAnchor, | |
topConstant: 12, | |
leading: anotherView.leadingAnchor, | |
leadingConstant: .greaterThan(12), | |
trailing: yetAnotherView.trailingAnchor, | |
trailingConstant: .lessThan(12) | |
) | |
*/ | |
public enum RelativeAnchor: ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral { | |
public typealias FloatLiteralType = Float | |
public typealias IntegerLiteralType = Int | |
public init(integerLiteral value: Self.IntegerLiteralType) { | |
self = .constant(.equalTo(CGFloat(value))) | |
} | |
public init(floatLiteral value: Self.FloatLiteralType) { | |
self = .constant(.equalTo(CGFloat(value))) | |
} | |
case constant(RelativeAnchorValue) | |
case multiplier(RelativeAnchorValue) | |
} | |
public enum RelativeAnchorValue: ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral { | |
public typealias FloatLiteralType = Float | |
public typealias IntegerLiteralType = Int | |
public init(integerLiteral value: Self.IntegerLiteralType) { | |
self = .equalTo(CGFloat(value)) | |
} | |
public init(floatLiteral value: Self.FloatLiteralType) { | |
self = .equalTo(CGFloat(value)) | |
} | |
case equalTo(_ value: CGFloat) | |
case greaterThan(_ value: CGFloat) | |
case lessThan(_ value: CGFloat) | |
public var value: CGFloat { | |
switch self { | |
case .equalTo(let value): | |
return value | |
case .greaterThan(let value): | |
return value | |
case .lessThan(let value): | |
return value | |
} | |
} | |
} | |
public enum Anchor { | |
case topTo(NSLayoutYAxisAnchor, RelativeAnchor = .constant(.equalTo(0))) | |
case leadingTo(NSLayoutXAxisAnchor, RelativeAnchor = .constant(.equalTo(0))) | |
case bottomTo(NSLayoutYAxisAnchor, RelativeAnchor = .constant(.equalTo(0))) | |
case trailingTo(NSLayoutXAxisAnchor, RelativeAnchor = .constant(.equalTo(0))) | |
case width(RelativeAnchor = .constant(.equalTo(0))) | |
case height(RelativeAnchor = .constant(.equalTo(0))) | |
case widthTo(NSLayoutDimension, RelativeAnchor = .constant(.equalTo(0))) | |
case heightTo(NSLayoutDimension, RelativeAnchor = .constant(.equalTo(0))) | |
case centerYTo(NSLayoutYAxisAnchor, RelativeAnchor = .constant(.equalTo(0))) | |
case centerXTo(NSLayoutXAxisAnchor, RelativeAnchor = .constant(.equalTo(0))) | |
} | |
public extension UIView { | |
@available(iOS 9, *) @discardableResult | |
func advancedAnchor(_ anchors: Anchor...) -> [NSLayoutConstraint] { | |
translatesAutoresizingMaskIntoConstraints = false | |
var constraints = [NSLayoutConstraint]() | |
for relativeAnchor in anchors { | |
switch relativeAnchor { | |
case .topTo(let top, let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(topAnchor.constraint(equalTo: top, constant: constant)) | |
case .greaterThan(let constant): | |
constraints.append(topAnchor.constraint(greaterThanOrEqualTo: top, constant: constant)) | |
case .lessThan(let constant): | |
constraints.append(topAnchor.constraint(lessThanOrEqualTo: top, constant: constant)) | |
} | |
case .multiplier(let relativeMultiplier): | |
switch relativeMultiplier { | |
case .equalTo(let multiplier): | |
constraints.append(topAnchor.constraint(equalToSystemSpacingBelow: top, multiplier: multiplier)) | |
case .greaterThan(let multiplier): | |
constraints.append(topAnchor.constraint(greaterThanOrEqualToSystemSpacingBelow: top, multiplier: multiplier)) | |
case .lessThan(let multiplier): | |
constraints.append(topAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: top, multiplier: multiplier)) | |
} | |
} | |
case .leadingTo(let leading, let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(leadingAnchor.constraint(equalTo: leading, constant: constant)) | |
case .greaterThan(let constant): | |
constraints.append(leadingAnchor.constraint(greaterThanOrEqualTo: leading, constant: constant)) | |
case .lessThan(let constant): | |
constraints.append(leadingAnchor.constraint(lessThanOrEqualTo: leading, constant: constant)) | |
} | |
case .multiplier(let relativeMultiplier): | |
switch relativeMultiplier { | |
case .equalTo(let multiplier): | |
constraints.append(leadingAnchor.constraint(equalToSystemSpacingAfter: leading, multiplier: multiplier)) | |
case .greaterThan(let multiplier): | |
constraints.append(leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: leading, multiplier: multiplier)) | |
case .lessThan(let multiplier): | |
constraints.append(leadingAnchor.constraint(lessThanOrEqualToSystemSpacingAfter: leading, multiplier: multiplier)) | |
} | |
} | |
case .bottomTo(let bottom, let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(bottomAnchor.constraint(equalTo: bottom, constant: -constant)) | |
case .greaterThan(let constant): | |
constraints.append(bottomAnchor.constraint(greaterThanOrEqualTo: bottom, constant: -constant)) | |
case .lessThan(let constant): | |
constraints.append(bottomAnchor.constraint(lessThanOrEqualTo: bottom, constant: -constant)) | |
} | |
case .multiplier(let relativeMultiplier): | |
switch relativeMultiplier { | |
case .equalTo(let multiplier): | |
constraints.append(bottomAnchor.constraint(equalToSystemSpacingBelow: bottom, multiplier: multiplier)) | |
case .greaterThan(let multiplier): | |
constraints.append(bottomAnchor.constraint(greaterThanOrEqualToSystemSpacingBelow: bottom, multiplier: multiplier)) | |
case .lessThan(let multiplier): | |
constraints.append(bottomAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: bottom, multiplier: multiplier)) | |
} | |
} | |
case .trailingTo(let trailing, let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(trailingAnchor.constraint(equalTo: trailing, constant: -constant)) | |
case .greaterThan(let constant): | |
constraints.append(trailingAnchor.constraint(greaterThanOrEqualTo: trailing, constant: -constant)) | |
case .lessThan(let constant): | |
constraints.append(trailingAnchor.constraint(lessThanOrEqualTo: trailing, constant: -constant)) | |
} | |
case .multiplier(let relativeMultiplier): | |
switch relativeMultiplier { | |
case .equalTo(let multiplier): | |
constraints.append(trailingAnchor.constraint(equalToSystemSpacingAfter: trailing, multiplier: multiplier)) | |
case .greaterThan(let multiplier): | |
constraints.append(trailingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: trailing, multiplier: multiplier)) | |
case .lessThan(let multiplier): | |
constraints.append(trailingAnchor.constraint(lessThanOrEqualToSystemSpacingAfter: trailing, multiplier: multiplier)) | |
} | |
} | |
case .width(let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(widthAnchor.constraint(equalToConstant: constant)) | |
case .greaterThan(let constant): | |
constraints.append(widthAnchor.constraint(greaterThanOrEqualToConstant: constant)) | |
case .lessThan(let constant): | |
constraints.append(widthAnchor.constraint(lessThanOrEqualToConstant: constant)) | |
} | |
case .multiplier(_): | |
assertionFailure("For multiplier values use .widthTo case instead") | |
} | |
case .height(let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(heightAnchor.constraint(equalToConstant: constant)) | |
case .greaterThan(let constant): | |
constraints.append(heightAnchor.constraint(greaterThanOrEqualToConstant: constant)) | |
case .lessThan(let constant): | |
constraints.append(heightAnchor.constraint(lessThanOrEqualToConstant: constant)) | |
} | |
case .multiplier(_): | |
assertionFailure("For multiplier values use .heightTo case instead") | |
} | |
case .widthTo(let width, let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(widthAnchor.constraint(equalTo: width, constant: constant)) | |
case .greaterThan(let constant): | |
constraints.append(widthAnchor.constraint(greaterThanOrEqualTo: width, constant: constant)) | |
case .lessThan(let constant): | |
constraints.append(widthAnchor.constraint(lessThanOrEqualTo: width, constant: constant)) | |
} | |
case .multiplier(let relativeMultiplier): | |
switch relativeMultiplier { | |
case .equalTo(let multiplier): | |
constraints.append(widthAnchor.constraint(equalTo: width, multiplier: multiplier)) | |
case .greaterThan(let multiplier): | |
constraints.append(widthAnchor.constraint(greaterThanOrEqualTo: width, multiplier: multiplier)) | |
case .lessThan(let multiplier): | |
constraints.append(widthAnchor.constraint(lessThanOrEqualTo: width, multiplier: multiplier)) | |
} | |
} | |
case .heightTo(let height, let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(heightAnchor.constraint(equalTo: height, constant: constant)) | |
case .greaterThan(let constant): | |
constraints.append(heightAnchor.constraint(greaterThanOrEqualTo: height, constant: constant)) | |
case .lessThan(let constant): | |
constraints.append(heightAnchor.constraint(lessThanOrEqualTo: height, constant: constant)) | |
} | |
case .multiplier(let relativeMultiplier): | |
switch relativeMultiplier { | |
case .equalTo(let multiplier): | |
constraints.append(heightAnchor.constraint(equalTo: height, multiplier: multiplier)) | |
case .greaterThan(let multiplier): | |
constraints.append(heightAnchor.constraint(greaterThanOrEqualTo: height, multiplier: multiplier)) | |
case .lessThan(let multiplier): | |
constraints.append(heightAnchor.constraint(lessThanOrEqualTo: height, multiplier: multiplier)) | |
} | |
} | |
case .centerYTo(let centerY, let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(centerYAnchor.constraint(equalTo: centerY, constant: constant)) | |
case .greaterThan(let constant): | |
constraints.append(centerYAnchor.constraint(greaterThanOrEqualTo: centerY, constant: constant)) | |
case .lessThan(let constant): | |
constraints.append(centerYAnchor.constraint(lessThanOrEqualTo: centerY, constant: constant)) | |
} | |
case .multiplier(let relativeMultiplier): | |
switch relativeMultiplier { | |
case .equalTo(let multiplier): | |
constraints.append(centerYAnchor.constraint(equalToSystemSpacingBelow: centerY, multiplier: multiplier)) | |
case .greaterThan(let multiplier): | |
constraints.append(centerYAnchor.constraint(greaterThanOrEqualToSystemSpacingBelow: centerY, multiplier: multiplier)) | |
case .lessThan(let multiplier): | |
constraints.append(centerYAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: centerY, multiplier: multiplier)) | |
} | |
} | |
case .centerXTo(let centerX, let relativeAnchor): | |
switch relativeAnchor { | |
case .constant(let relativeConstant): | |
switch relativeConstant { | |
case .equalTo(let constant): | |
constraints.append(centerXAnchor.constraint(equalTo: centerX, constant: constant)) | |
case .greaterThan(let constant): | |
constraints.append(centerXAnchor.constraint(greaterThanOrEqualTo: centerX, constant: constant)) | |
case .lessThan(let constant): | |
constraints.append(centerXAnchor.constraint(lessThanOrEqualTo: centerX, constant: constant)) | |
} | |
case .multiplier(let relativeMultiplier): | |
switch relativeMultiplier { | |
case .equalTo(let multiplier): | |
constraints.append(centerXAnchor.constraint(equalToSystemSpacingAfter: centerX, multiplier: multiplier)) | |
case .greaterThan(let multiplier): | |
constraints.append(centerXAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: centerX, multiplier: multiplier)) | |
case .lessThan(let multiplier): | |
constraints.append(centerXAnchor.constraint(lessThanOrEqualToSystemSpacingAfter: centerX, multiplier: multiplier)) | |
} | |
} | |
} | |
} | |
constraints.forEach({$0.isActive = true}) | |
return constraints | |
} | |
@available(iOS 9, *) @discardableResult | |
func advancedAnchor( | |
top: NSLayoutYAxisAnchor? = nil, | |
topConstant: RelativeAnchorValue? = nil, | |
leading: NSLayoutXAxisAnchor? = nil, | |
leadingConstant: RelativeAnchorValue? = nil, | |
bottom: NSLayoutYAxisAnchor? = nil, | |
bottomConstant: RelativeAnchorValue? = nil, | |
trailing: NSLayoutXAxisAnchor? = nil, | |
trailingConstant: RelativeAnchorValue? = nil, | |
widthConstant: RelativeAnchorValue? = nil, | |
heightConstant: RelativeAnchorValue? = nil) -> [NSLayoutConstraint] { | |
translatesAutoresizingMaskIntoConstraints = false | |
var anchors = [NSLayoutConstraint]() | |
if let top = top, let topConstant = topConstant { | |
switch topConstant { | |
case .equalTo(let constant): | |
anchors.append(topAnchor.constraint(equalTo: top, constant: constant)) | |
case .greaterThan(let constant): | |
anchors.append(topAnchor.constraint(greaterThanOrEqualTo: top, constant: constant)) | |
case .lessThan(let constant): | |
anchors.append(topAnchor.constraint(lessThanOrEqualTo: top, constant: constant)) | |
} | |
} | |
if let leading = leading, let leadingConstant = leadingConstant { | |
switch leadingConstant { | |
case .equalTo(let constant): | |
anchors.append(leadingAnchor.constraint(equalTo: leading, constant: constant)) | |
case .greaterThan(let constant): | |
anchors.append(leadingAnchor.constraint(greaterThanOrEqualTo: leading, constant: constant)) | |
case .lessThan(let constant): | |
anchors.append(leadingAnchor.constraint(lessThanOrEqualTo: leading, constant: constant)) | |
} | |
} | |
if let bottom = bottom, let bottomConstant = bottomConstant { | |
switch bottomConstant { | |
case .equalTo(let constant): | |
anchors.append(bottomAnchor.constraint(equalTo: bottom, constant: -constant)) | |
case .greaterThan(let constant): | |
anchors.append(bottomAnchor.constraint(greaterThanOrEqualTo: bottom, constant: -constant)) | |
case .lessThan(let constant): | |
anchors.append(bottomAnchor.constraint(lessThanOrEqualTo: bottom, constant: -constant)) | |
} | |
} | |
if let trailing = trailing, let trailingConstant = trailingConstant { | |
switch trailingConstant { | |
case .equalTo(let constant): | |
anchors.append(trailingAnchor.constraint(equalTo: trailing, constant: -constant)) | |
case .greaterThan(let constant): | |
anchors.append(trailingAnchor.constraint(greaterThanOrEqualTo: trailing, constant: -constant)) | |
case .lessThan(let constant): | |
anchors.append(trailingAnchor.constraint(lessThanOrEqualTo: trailing, constant: -constant)) | |
} | |
} | |
if let widthConstant = widthConstant, widthConstant.value > 0 { | |
switch widthConstant { | |
case .equalTo(let constant): | |
anchors.append(widthAnchor.constraint(equalToConstant: constant)) | |
case .greaterThan(let constant): | |
anchors.append(widthAnchor.constraint(greaterThanOrEqualToConstant: constant)) | |
case .lessThan(let constant): | |
anchors.append(widthAnchor.constraint(lessThanOrEqualToConstant: constant)) | |
} | |
} | |
if let heightConstant = heightConstant, heightConstant.value > 0 { | |
switch heightConstant { | |
case .equalTo(let constant): | |
anchors.append(heightAnchor.constraint(equalToConstant: constant)) | |
case .greaterThan(let constant): | |
anchors.append(heightAnchor.constraint(greaterThanOrEqualToConstant: constant)) | |
case .lessThan(let constant): | |
anchors.append(heightAnchor.constraint(lessThanOrEqualToConstant: constant)) | |
} | |
} | |
anchors.forEach({$0.isActive = true}) | |
return anchors | |
} | |
func fillToSuperview(insets: UIEdgeInsets = .zero) { | |
translatesAutoresizingMaskIntoConstraints = false | |
if let superview = superview { | |
let left = leftAnchor.constraint(equalTo: superview.leftAnchor, constant: insets.left) | |
let right = rightAnchor.constraint(equalTo: superview.rightAnchor, constant: -insets.right) | |
let top = topAnchor.constraint(equalTo: superview.topAnchor, constant: insets.top) | |
let bottom = bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: -insets.bottom) | |
NSLayoutConstraint.activate([left, right, top, bottom]) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment