Created
May 3, 2021 16:40
-
-
Save bricker/1a0999ceb929d0300368198ce3fb7ff2 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
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