Skip to content

Instantly share code, notes, and snippets.

@Kievkao
Created October 21, 2021 11:02
Show Gist options
  • Save Kievkao/463e2b064b450c608fde4075c2e342f8 to your computer and use it in GitHub Desktop.
Save Kievkao/463e2b064b450c608fde4075c2e342f8 to your computer and use it in GitHub Desktop.
//
// UIView+Helpers.swift
// DMLOOYFG
//
// Created by Roman Rechich on 07.10.2021.
//
import UIKit
import SwiftUI
public protocol LayoutAnchor {
func constraint(equalTo anchor: Self, constant: CGFloat) -> NSLayoutConstraint
func constraint(greaterThanOrEqualTo anchor: Self, constant: CGFloat) -> NSLayoutConstraint
func constraint(lessThanOrEqualTo anchor: Self, constant: CGFloat) -> NSLayoutConstraint
}
public protocol LayoutDimension {
func constraint(equalToConstant constant: CGFloat) -> NSLayoutConstraint
func constraint(equalTo anchor: Self, multiplier: CGFloat, constant: CGFloat) -> NSLayoutConstraint
func constraint(greaterThanOrEqualTo anchor: Self, multiplier: CGFloat, constant: CGFloat) -> NSLayoutConstraint
func constraint(lessThanOrEqualTo anchor: Self, multiplier: CGFloat, constant: CGFloat) -> NSLayoutConstraint
}
extension NSLayoutAnchor: LayoutAnchor {}
extension NSLayoutDimension: LayoutDimension {}
public struct LayoutProperty<Anchor: LayoutAnchor> {
fileprivate let anchor: Anchor
}
public struct LayoutDimensionProperty<Anchor: LayoutDimension> {
fileprivate let anchor: Anchor
}
public class LayoutProxy {
public lazy var leading = property(with: view.leadingAnchor)
public lazy var trailing = property(with: view.trailingAnchor)
public lazy var top = property(with: view.topAnchor)
public lazy var bottom = property(with: view.bottomAnchor)
public lazy var centerX = property(with: view.centerXAnchor)
public lazy var centerY = property(with: view.centerYAnchor)
public lazy var width = propertyDimension(with: view.widthAnchor)
public lazy var height = propertyDimension(with: view.heightAnchor)
public lazy var safeLeading = property(with: view.safeAreaLayoutGuide.leadingAnchor)
public lazy var safeTrailing = property(with: view.safeAreaLayoutGuide.trailingAnchor)
public lazy var safeTop = property(with: view.safeAreaLayoutGuide.topAnchor)
public lazy var safeBottom = property(with: view.safeAreaLayoutGuide.bottomAnchor)
public lazy var safeCenterX = property(with: view.safeAreaLayoutGuide.centerXAnchor)
public lazy var safeCenterY = property(with: view.safeAreaLayoutGuide.centerYAnchor)
public lazy var safeWidth = propertyDimension(with: view.safeAreaLayoutGuide.widthAnchor)
public lazy var safeHeight = propertyDimension(with: view.safeAreaLayoutGuide.heightAnchor)
private let view: UIView
fileprivate init(view: UIView) {
self.view = view
}
private func property<A: LayoutAnchor>(with anchor: A) -> LayoutProperty<A> {
return LayoutProperty(anchor: anchor)
}
private func propertyDimension<A: LayoutDimension>(with anchor: A) -> LayoutDimensionProperty<A> {
return LayoutDimensionProperty(anchor: anchor)
}
}
public extension LayoutProperty {
func equal(to otherAnchor: Anchor, offsetBy constant: CGFloat = 0) {
anchor.constraint(equalTo: otherAnchor, constant: constant).isActive = true
}
func greaterThanOrEqual(to otherAnchor: Anchor, offsetBy constant: CGFloat = 0) {
anchor.constraint(greaterThanOrEqualTo: otherAnchor, constant: constant).isActive = true
}
func lessThanOrEqual(to otherAnchor: Anchor, offsetBy constant: CGFloat = 0) {
anchor.constraint(lessThanOrEqualTo: otherAnchor, constant: constant).isActive = true
}
}
public extension LayoutDimensionProperty {
func equal(to constant: CGFloat) {
anchor.constraint(equalToConstant: constant).isActive = true
}
func equal(to otherAnchor: Anchor, multiplier: CGFloat = 1, offsetBy constant: CGFloat = 0) {
anchor.constraint(equalTo: otherAnchor, multiplier: multiplier, constant: constant).isActive = true
}
func greaterThanOrEqual(to otherAnchor: Anchor, multiplier: CGFloat = 1, offsetBy constant: CGFloat = 0) {
anchor.constraint(greaterThanOrEqualTo: otherAnchor, multiplier: multiplier, constant: constant).isActive = true
}
func lessThanOrEqual(to otherAnchor: Anchor, multiplier: CGFloat = 1, offsetBy constant: CGFloat = 0) {
anchor.constraint(lessThanOrEqualTo: otherAnchor, multiplier: multiplier, constant: constant).isActive = true
}
}
public extension UIView {
func layout(using closure: (LayoutProxy) -> Void) {
translatesAutoresizingMaskIntoConstraints = false
closure(LayoutProxy(view: self))
}
func layoutFill(in view: UIView, edgeInsets: UIEdgeInsets = .zero) {
self.layout {
$0.leading == view.leadingAnchor + edgeInsets.left
$0.trailing == view.trailingAnchor - edgeInsets.right
$0.top == view.topAnchor + edgeInsets.top
$0.bottom == view.bottomAnchor - edgeInsets.bottom
}
}
func layoutFill(
in view: UIView,
edgeInsets: UIEdgeInsets = .zero,
safeLeading: Bool = false,
safeTrailing: Bool = false,
safeTop: Bool = false,
safeBottom: Bool = false
) {
self.layout {
if safeLeading {
$0.safeLeading == view.safeAreaLayoutGuide.leadingAnchor + edgeInsets.left
} else {
$0.leading == view.leadingAnchor + edgeInsets.left
}
if safeTrailing {
$0.safeTrailing == view.safeAreaLayoutGuide.trailingAnchor + edgeInsets.left
} else {
$0.trailing == view.trailingAnchor + edgeInsets.left
}
if safeTop {
$0.safeTop == view.safeAreaLayoutGuide.topAnchor + edgeInsets.left
} else {
$0.top == view.topAnchor + edgeInsets.left
}
if safeBottom {
$0.safeBottom == view.safeAreaLayoutGuide.bottomAnchor + edgeInsets.left
} else {
$0.bottom == view.bottomAnchor + edgeInsets.left
}
}
}
}
// MARK: - Anchor
public func +<A: LayoutAnchor>(lhs: A, rhs: CGFloat) -> (A, CGFloat) {
(lhs, rhs)
}
public func -<A: LayoutAnchor>(lhs: A, rhs: CGFloat) -> (A, CGFloat) {
(lhs, -rhs)
}
public func ==<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: (A, CGFloat)) {
lhs.equal(to: rhs.0, offsetBy: rhs.1)
}
public func ==<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: A) {
lhs.equal(to: rhs)
}
public func >=<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: (A, CGFloat)) {
lhs.greaterThanOrEqual(to: rhs.0, offsetBy: rhs.1)
}
public func >=<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: A) {
lhs.greaterThanOrEqual(to: rhs)
}
public func <=<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: (A, CGFloat)) {
lhs.lessThanOrEqual(to: rhs.0, offsetBy: rhs.1)
}
public func <=<A: LayoutAnchor>(lhs: LayoutProperty<A>, rhs: A) {
lhs.lessThanOrEqual(to: rhs)
}
// MARK: - Dimension
public func ==<A: LayoutDimension>(lhs: LayoutDimensionProperty<A>, rhs: CGFloat) {
lhs.equal(to: rhs)
}
public func ==<A: LayoutDimension>(lhs: LayoutDimensionProperty<A>, rhs: A) {
lhs.equal(to: rhs)
}
public func ==<A: LayoutDimension>(lhs: LayoutDimensionProperty<A>, rhs: (A, CGFloat)) {
lhs.equal(to: rhs.0, multiplier: rhs.1)
}
public func ==<A: LayoutDimension>(lhs: LayoutDimensionProperty<A>, rhs: (A, CGFloat, CGFloat)) {
lhs.equal(to: rhs.0, multiplier: rhs.1, offsetBy: rhs.2)
}
public func >=<A: LayoutDimension>(lhs: LayoutDimensionProperty<A>, rhs: A) {
lhs.greaterThanOrEqual(to: rhs)
}
public func <=<A: LayoutDimension>(lhs: LayoutDimensionProperty<A>, rhs: A) {
lhs.lessThanOrEqual(to: rhs)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment