Skip to content

Instantly share code, notes, and snippets.

@reeichert
Last active September 7, 2022 00:20
Show Gist options
  • Save reeichert/54471633cbc03cf77009424d79d0eef3 to your computer and use it in GitHub Desktop.
Save reeichert/54471633cbc03cf77009424d79d0eef3 to your computer and use it in GitHub Desktop.
AutoLayout helpers
//
// File.swift
//
//
// Created by João Reichert on 06/09/22.
//
import UIKit
public extension UIView {
@discardableResult
func subviews(_ subViews: UIView...) -> UIView {
subviews(subViews)
}
@discardableResult
@objc
func subviews(_ subViews: [UIView]) -> UIView {
for sv in subViews {
addSubview(sv)
sv.translatesAutoresizingMaskIntoConstraints = false
}
return self
}
}
public extension UITableViewCell {
@discardableResult
override func subviews(_ subViews: [UIView]) -> UIView {
contentView.subviews(subViews)
}
}
public extension UICollectionViewCell {
@discardableResult
override func subviews(_ subViews: [UIView]) -> UIView {
contentView.subviews(subViews)
}
}
public extension UIStackView {
@discardableResult
private func arrangedSubviews(_ subViews: UIView...) -> UIView {
arrangedSubviews(subViews)
}
@discardableResult
func arrangedSubviews(_ subViews: [UIView]) -> UIView {
subViews.forEach { addArrangedSubview($0) }
return self
}
}
public extension UIView {
@discardableResult
func left(_ points: Double = 0.0) -> Self {
position(.left, points: points)
}
@discardableResult
func leftSafe(_ points: Double = 0.0) -> Self {
positionSafe(.left, points: points)
}
@discardableResult
func left(equalTo anchor: NSLayoutXAxisAnchor, constant: Double = 0.0) -> Self {
self.leftAnchor.constraint(equalTo: anchor, constant: constant).isActive = true
return self
}
@discardableResult
func left(equalTo view: UIView, constant: Double = 0.0) -> Self {
self.leftAnchor.constraint(equalTo: view.leftAnchor, constant: constant).isActive = true
return self
}
@discardableResult
func leftToRight(of view: UIView, constant: Double = 0.0) -> Self {
self.leftAnchor.constraint(equalTo: view.rightAnchor, constant: constant).isActive = true
return self
}
@discardableResult
func right(_ points: Double = 0.0) -> Self {
position(.right, points: -points)
}
@discardableResult
func rightSafe(_ points: Double = 0.0) -> Self {
positionSafe(.right, points: -points)
}
@discardableResult
func right(equalTo anchor: NSLayoutXAxisAnchor, constant: Double = 0.0) -> Self {
self.rightAnchor.constraint(equalTo: anchor, constant: -constant).isActive = true
return self
}
@discardableResult
func right(equalTo view: UIView, constant: Double = 0.0) -> Self {
self.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -constant).isActive = true
return self
}
@discardableResult
func rightToLeft(of view: UIView, constant: Double = 0.0) -> Self {
self.rightAnchor.constraint(equalTo: view.leftAnchor, constant: -constant).isActive = true
return self
}
@discardableResult
func top(_ points: Double = 0.0) -> Self {
position(.top, points: points)
}
@discardableResult
func topSafe(_ points: Double = 0.0) -> Self {
positionSafe(.top, points: points)
}
@discardableResult
func top(equalTo anchor: NSLayoutYAxisAnchor, constant: Double = 0.0) -> Self {
self.topAnchor.constraint(equalTo: anchor, constant: constant).isActive = true
return self
}
@discardableResult
func top(equalTo view: UIView, constant: Double = 0.0) -> Self {
self.topAnchor.constraint(equalTo: view.topAnchor, constant: constant).isActive = true
return self
}
@discardableResult
func topToBottom(of view: UIView, constant: Double = 0.0) -> Self {
self.topAnchor.constraint(equalTo: view.bottomAnchor, constant: constant).isActive = true
return self
}
@discardableResult
func bottom(_ points: Double = 0.0) -> Self {
position(.bottom, points: -points)
}
@discardableResult
func bottomSafe(_ points: Double = 0.0) -> Self {
positionSafe(.bottom, points: -points)
}
@discardableResult
func bottom(equalTo anchor: NSLayoutYAxisAnchor, constant: Double = 0.0) -> Self {
self.bottomAnchor.constraint(equalTo: anchor, constant: -constant).isActive = true
return self
}
@discardableResult
func bottom(equalTo view: UIView, constant: Double = 0.0) -> Self {
self.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -constant).isActive = true
return self
}
@discardableResult
func bottomToTop(of view: UIView, constant: Double = 0.0) -> Self {
self.bottomAnchor.constraint(equalTo: view.topAnchor, constant: -constant).isActive = true
return self
}
@discardableResult
func leading(_ points: Double = 0.0) -> Self {
position(.leading, points: points)
}
@discardableResult
func leadingSafe(_ points: Double = 0.0) -> Self {
positionSafe(.leading, points: points)
}
@discardableResult
func leading(equalTo anchor: NSLayoutXAxisAnchor, constant: Double = 0.0) -> Self {
self.leadingAnchor.constraint(equalTo: anchor, constant: constant).isActive = true
return self
}
@discardableResult
func leading(equalTo view: UIView, constant: Double = 0.0) -> Self {
self.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: constant).isActive = true
return self
}
@discardableResult
func trailing(_ points: Double = 0.0) -> Self {
position(.trailing, points: -points)
}
@discardableResult
func trailingSafe(_ points: Double = 0.0) -> Self {
positionSafe(.trailing, points: -points)
}
@discardableResult
func trailing(equalTo anchor: NSLayoutXAxisAnchor, constant: Double = 0.0) -> Self {
self.trailingAnchor.constraint(equalTo: anchor, constant: -constant).isActive = true
return self
}
@discardableResult
func trailing(equalTo view: UIView, constant: Double = 0.0) -> Self {
self.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -constant).isActive = true
return self
}
fileprivate func position(_ position: NSLayoutConstraint.Attribute,
relatedBy: NSLayoutConstraint.Relation = .equal,
points: Double) -> Self {
if let spv = superview {
let c = constraint(item: self, attribute: position,
relatedBy: relatedBy,
toItem: spv,
constant: points)
spv.addConstraint(c)
}
return self
}
fileprivate func positionSafe(_ position: NSLayoutConstraint.Attribute,
points: Double) -> Self {
guard let spv = superview else { return self }
switch position {
case .left:
self.leftAnchor.constraint(
equalTo: spv.safeAreaLayoutGuide.leftAnchor,
constant: points).isActive = true
case .right:
self.rightAnchor.constraint(
equalTo: spv.safeAreaLayoutGuide.rightAnchor,
constant: points).isActive = true
case .top:
self.topAnchor.constraint(
equalTo: spv.safeAreaLayoutGuide.topAnchor,
constant: points).isActive = true
case .bottom:
self.bottomAnchor.constraint(
equalTo: spv.safeAreaLayoutGuide.bottomAnchor,
constant: points).isActive = true
case .leading:
self.leadingAnchor.constraint(
equalTo: spv.safeAreaLayoutGuide.leadingAnchor,
constant: points).isActive = true
case .trailing:
self.trailingAnchor.constraint(
equalTo: spv.safeAreaLayoutGuide.trailingAnchor,
constant: points).isActive = true
default:
break
}
return self
}
fileprivate func constraint(item view1: AnyObject,
attribute attr1: NSLayoutConstraint.Attribute,
relatedBy: NSLayoutConstraint.Relation = .equal,
toItem view2: AnyObject? = nil,
attribute attr2: NSLayoutConstraint.Attribute? = nil, // Not an attribute??
multiplier: Double = 1,
constant: Double = 0) -> NSLayoutConstraint {
let c = NSLayoutConstraint(item: view1, attribute: attr1,
relatedBy: relatedBy,
toItem: view2, attribute: ((attr2 == nil) ? attr1 : attr2! ),
multiplier: CGFloat(multiplier), constant: CGFloat(constant))
c.priority = UILayoutPriority(rawValue: UILayoutPriority.defaultHigh.rawValue + 1)
return c
}
@discardableResult
func fillContainer(padding: Double = 0.0) -> Self {
fillHorizontally(padding: padding)
fillVertically(padding: padding)
return self
}
@discardableResult
func fillVertically(padding: Double = 0.0) -> Self {
fill(.vertical, points: padding)
}
@discardableResult
func fillHorizontally(padding: Double = 0.0) -> Self {
fill(.horizontal, points: padding)
}
fileprivate func fill(_ axis: NSLayoutConstraint.Axis, points: Double = 0) -> Self {
let a: NSLayoutConstraint.Attribute = axis == .vertical ? .top : .leading
let b: NSLayoutConstraint.Attribute = axis == .vertical ? .bottom : .trailing
if let spv = superview {
let c1 = constraint(item: self, attribute: a, toItem: spv, constant: points)
let c2 = constraint(item: self, attribute: b, toItem: spv, constant: -points)
spv.addConstraints([c1, c2])
}
return self
}
@discardableResult
func size(_ points: Double) -> Self {
width(points)
height(points)
return self
}
@discardableResult
func height(_ points: Double) -> Self {
size(.height, points: points)
}
@discardableResult
func height(equal view: UIView) -> Self {
self.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
return self
}
@discardableResult
func width(_ points: Double) -> Self {
size(.width, points: points)
}
@discardableResult
func width(equal view: UIView) -> Self {
self.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
return self
}
fileprivate func size(_ attribute: NSLayoutConstraint.Attribute,
relatedBy: NSLayoutConstraint.Relation = .equal,
points: Double) -> Self {
let c = constraint(item: self,
attribute: attribute,
relatedBy: relatedBy,
constant: points)
if let spv = superview {
spv.addConstraint(c)
} else {
addConstraint(c)
}
return self
}
@discardableResult
func centerHorizontally(offset: Double = 0.0) -> Self {
if let spv = superview {
align(.vertical, v1: self, with: spv, offset: offset)
}
return self
}
@discardableResult
func centerHorizontally(equalTo anchor: NSLayoutXAxisAnchor, constant: Double = 0.0) -> Self {
self.centerXAnchor.constraint(equalTo: anchor, constant: constant).isActive = true
return self
}
@discardableResult
func centerHorizontally(with view: UIView, constant: Double = 0.0) -> Self {
self.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: constant).isActive = true
return self
}
@discardableResult
func centerVertically(offset: Double = 0.0) -> Self {
if let spv = superview {
align(.horizontal, v1: self, with: spv, offset: offset)
}
return self
}
@discardableResult
func centerVertically(equalTo anchor: NSLayoutYAxisAnchor, constant: Double = 0.0) -> Self {
self.centerYAnchor.constraint(equalTo: anchor, constant: constant).isActive = true
return self
}
@discardableResult
func centerVertically(with view: UIView, constant: Double = 0.0) -> Self {
self.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: constant).isActive = true
return self
}
fileprivate func align(_ axis: NSLayoutConstraint.Axis, v1: UIView, with v2: UIView, offset: Double) {
if let spv = v1.superview {
let center: NSLayoutConstraint.Attribute = axis == .horizontal ? .centerY : .centerX
let c = constraint(item: v1, attribute: center, toItem: v2, constant: offset)
spv.addConstraint(c)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment