Skip to content

Instantly share code, notes, and snippets.

@bricker
Created May 3, 2021 16:40
Show Gist options
  • Save bricker/1a0999ceb929d0300368198ce3fb7ff2 to your computer and use it in GitHub Desktop.
Save bricker/1a0999ceb929d0300368198ce3fb7ff2 to your computer and use it in GitHub Desktop.
import Foundation
import UIKit
class ConstraintTransaction {
private var constraints: [Constraint] = []
func add(_ constraint: Constraint) {
constraints.append(constraint)
}
func activate() {
NSLayoutConstraint.activate(constraints.flatMap(\.constraints))
}
}
class Constraint {
private(set) var constraints: [NSLayoutConstraint] = []
private let view: UIView
private var isBatched = false
init(_ view: UIView) {
self.view = view
view.translatesAutoresizingMaskIntoConstraints = false
}
func embedded(in parentView: UIView) -> Self {
parentView.addSubview(view)
return self
}
func ratio(_ multiplier: CGFloat) -> Self {
let constraint = view.heightAnchor.constraint(equalTo: view.widthAnchor, multiplier: multiplier, constant: 0)
constraints.append(constraint)
return self
}
func centeredX(to parentView: UIView, offset: CGFloat = 0) -> Self {
parentView.addSubview(view)
let constraint = view.centerXAnchor.constraint(equalTo: parentView.centerXAnchor, constant: offset)
constraints.append(constraint)
return self
}
func centeredY(to parentView: UIView, offset: CGFloat = 0) -> Self {
let constraint = view.centerYAnchor.constraint(equalTo: parentView.centerYAnchor, constant: offset)
constraints.append(constraint)
return self
}
func maxHeight(_ constant: CGFloat) -> Self {
let constraint = view.heightAnchor.constraint(lessThanOrEqualToConstant: constant)
constraints.append(constraint)
return self
}
func minHeight(_ constant: CGFloat) -> Self {
let constraint = view.heightAnchor.constraint(greaterThanOrEqualToConstant: constant)
constraints.append(constraint)
return self
}
func maxWidth(_ constant: CGFloat) -> Self {
let constraint = view.widthAnchor.constraint(lessThanOrEqualToConstant: constant)
constraints.append(constraint)
return self
}
func minWidth(_ constant: CGFloat) -> Self {
let constraint = view.widthAnchor.constraint(greaterThanOrEqualToConstant: constant)
constraints.append(constraint)
return self
}
func fixedWidth(_ constant: CGFloat) -> Self {
let constraint = view.widthAnchor.constraint(equalToConstant: constant)
constraints.append(constraint)
return self
}
func fixedHeight(_ constant: CGFloat) -> Self {
let constraint = view.heightAnchor.constraint(equalToConstant: constant)
constraints.append(constraint)
return self
}
func minMarginX(_ constant: CGFloat, from parentView: UIView) -> Self {
let constraintL = view.leadingAnchor.constraint(greaterThanOrEqualTo: parentView.leadingAnchor, constant: constant)
constraints.append(constraintL)
let constraintR = view.trailingAnchor.constraint(lessThanOrEqualTo: parentView.trailingAnchor, constant: -constant)
constraints.append(constraintR)
return self
}
func above(_ otherView: UIView, by constant: CGFloat = 0) -> Self {
let constraint = view.bottomAnchor.constraint(equalTo: otherView.topAnchor, constant: -constant)
constraints.append(constraint)
return self
}
func below(_ otherView: UIView, by constant: CGFloat = 0) -> Self {
let constraint = view.topAnchor.constraint(equalTo: otherView.bottomAnchor, constant: constant)
constraints.append(constraint)
return self
}
func left(of otherView: UIView, by constant: CGFloat = 0) -> Self {
let constraint = view.trailingAnchor.constraint(equalTo: otherView.leadingAnchor, constant: -constant)
constraints.append(constraint)
return self
}
func right(of otherView: UIView, by constant: CGFloat = 0) -> Self {
let constraint = view.leadingAnchor.constraint(equalTo: otherView.trailingAnchor, constant: constant)
constraints.append(constraint)
return self
}
// Pin a view's top edge to the top edge of a parent view, with optional *absolute* padding
func pinnedToTop(of parentView: UIView, padding: CGFloat = 0) -> Self {
let constraint = view.topAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.topAnchor, constant: padding)
constraints.append(constraint)
return self
}
// Pin a view's bottom edge to the bottom edge of a parent view, with optional *absolute* padding
func pinnedToBottom(of parentView: UIView, padding: CGFloat = 0) -> Self {
let constraint = view.bottomAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.bottomAnchor, constant: -padding)
constraints.append(constraint)
return self
}
// Pin a view's leading edge to the leading edge of a parent view, with optional *absolute* padding
func pinnedToLeft(of parentView: UIView, padding: CGFloat = 0) -> Self {
let constraint = view.leadingAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.leadingAnchor, constant: padding)
constraints.append(constraint)
return self
}
// Pin a view's trailing edge to the trailing edge of a parent view, with optional *absolute* padding
func pinnedToRight(of parentView: UIView, padding: CGFloat = 0) -> Self {
let constraint = view.trailingAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.trailingAnchor, constant: -padding)
constraints.append(constraint)
return self
}
func fillingWidth(of parentView: UIView, padding: CGFloat = 0) -> Self {
_ = pinnedToLeft(of: parentView, padding: padding)
_ = pinnedToRight(of: parentView, padding: padding)
return self
}
func fillingHeight(of parentView: UIView, padding: CGFloat = 0) -> Self {
_ = pinnedToTop(of: parentView, padding: padding)
_ = pinnedToBottom(of: parentView, padding: padding)
return self
}
func pinned(to parentView: UIView, verticalPadding: CGFloat = 0, horizontalPadding: CGFloat = 0) -> Self {
constraints.append(view.topAnchor.constraint(equalTo: parentView.topAnchor, constant: verticalPadding))
constraints.append(view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor, constant: -verticalPadding))
constraints.append(view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: horizontalPadding))
constraints.append(view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -horizontalPadding))
return self
}
func pinnedToSafeArea(of parentView: UIView, verticalPadding: CGFloat = 0, horizontalPadding: CGFloat = 0,
bottomPriority: Float = 1000, leftPriority: Float = 1000) -> Self
{
let bottom = view.bottomAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.bottomAnchor, constant: -verticalPadding)
bottom.priority = UILayoutPriority(bottomPriority)
let left = view.leadingAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.leadingAnchor, constant: horizontalPadding)
left.priority = UILayoutPriority(leftPriority)
let right = view.trailingAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.trailingAnchor, constant: -horizontalPadding)
let top = view.topAnchor.constraint(equalTo: parentView.safeAreaLayoutGuide.topAnchor, constant: verticalPadding)
constraints.append(left)
constraints.append(bottom)
constraints.append(right)
constraints.append(top)
return self
}
func with(_ constraint: (UIView) -> NSLayoutConstraint) -> Self {
let result = constraint(view)
constraints.append(result)
return self
}
// Activate constraints for this view, immediately.
// Consider using activate(with:) instead to acivate all constraints for all views at the same time.
func activate() {
NSLayoutConstraint.activate(constraints)
}
func activate(with batch: ConstraintTransaction) {
batch.add(self)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment