Skip to content

Instantly share code, notes, and snippets.

@ATahhan
Last active July 5, 2023 13:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ATahhan/9c8e65d9c4bace74c33ecfb4038d1958 to your computer and use it in GitHub Desktop.
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
//
// 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