Created
October 21, 2021 11:02
-
-
Save Kievkao/463e2b064b450c608fde4075c2e342f8 to your computer and use it in GitHub Desktop.
This file contains 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
// | |
// 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