jsdw_ios/QuickLocation/Tool/AutoLayout/AutoLayout+UIView.swift

511 lines
20 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// AutoLayout_UIView.swift
// AutoLayoutSwiftDemo
//
// Created by Johnhao on 2022/1/6.
//
import UIKit
// MARK: - AutoLayout
extension UIView {
// MARK: - Private
private enum Key {
/// key
internal static var lastConstraint = "lastConstraint"
/// key
internal static var constraints = "constraints"
/// jhLayoutChain
internal static var jhLayoutChain = "jhLayoutChain"
/// collapsed
internal static var collapsed = "collapsed"
/// autoCollapsed
internal static var autoCollapse = "autoCollapse"
/// hiddenCollapsed
internal static var hiddenCollapse = "hiddenCollapse"
}
///
private var jh_allConstraints: [NSLayoutConstraint] {
get {
jh_layoutConstraints.values.compactMap { $0 }
}
}
/// key-value
private var jh_layoutConstraints: [String: NSLayoutConstraint] {
get {
guard let constraints = objc_getAssociatedObject(self, &Key.constraints) as? [String: NSLayoutConstraint] else {
let constraints = [String: NSLayoutConstraint]()
objc_setAssociatedObject(self, &Key.constraints, constraints, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return constraints
}
return constraints
}
set {
objc_setAssociatedObject(self, &Key.constraints, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
@discardableResult
private func jh_constrain(attribute: NSLayoutConstraint.Attribute,
toSuperView: UIView?,
constant: CGFloat,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
var offset = constant
var new_relation = relation
if attribute == .bottom || attribute == .right || attribute == .trailing {
offset = -constant
if relation == .lessThanOrEqual {
new_relation = .greaterThanOrEqual
} else if relation == .greaterThanOrEqual {
new_relation = .lessThanOrEqual
}
}
return jh_constrain(attribute: attribute,
toAttribute: attribute,
otherView: toSuperView,
multiplier: 1.0,
constant: offset,
relation: new_relation)
}
@discardableResult
private func jh_constrain(attribute: NSLayoutConstraint.Attribute,
toAttribute: NSLayoutConstraint.Attribute,
otherView: UIView?,
multiplier: CGFloat,
constant: CGFloat,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
self.translatesAutoresizingMaskIntoConstraints = false
let layoutKey = "\(attribute.rawValue)-\(relation.rawValue)-\((otherView?.hash ?? 0))-\(toAttribute.rawValue)-\(multiplier)"
//
guard let constraint = self.jh_layoutConstraints[layoutKey] else {
//
let constraint = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: relation, toItem: otherView, attribute: toAttribute, multiplier: multiplier, constant: constant)
self.jh_layoutConstraints[layoutKey] = constraint
self.jh_lastConstraint = constraint
constraint.isActive = true
return constraint
}
//
constraint.constant = constant
self.jh_lastConstraint = constraint
constraint.isActive = true
return constraint
}
}
// MARK: - Public
extension UIView {
@discardableResult
public func jh_constrainAttribute(_ attribute: NSLayoutConstraint.Attribute,
toAttribute: NSLayoutConstraint.Attribute,
of otherView: UIView) -> NSLayoutConstraint {
jh_constrainAttribute(attribute,
toAttribute: toAttribute,
of: otherView,
offset: 1.0)
}
@discardableResult
public func jh_constrainAttribute(_ attribute: NSLayoutConstraint.Attribute,
toAttribute: NSLayoutConstraint.Attribute,
of otherView: UIView,
offset: CGFloat) -> NSLayoutConstraint {
jh_constrainAttribute(attribute,
toAttribute: toAttribute,
of: otherView,
offset: offset,
relation: .equal)
}
@discardableResult
public func jh_constrainAttribute(_ attribute: NSLayoutConstraint.Attribute,
toAttribute: NSLayoutConstraint.Attribute,
of otherView: UIView?,
offset: CGFloat,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
jh_constrain(attribute: attribute,
toAttribute: toAttribute,
otherView: otherView,
multiplier: 1.0,
constant: offset,
relation: relation)
}
@discardableResult
public func jh_constrainAttribute(_ attribute: NSLayoutConstraint.Attribute,
toAttribute: NSLayoutConstraint.Attribute,
of otherView: UIView,
multiplier: CGFloat) -> NSLayoutConstraint {
jh_constrainAttribute(attribute,
toAttribute: toAttribute,
of: otherView,
multiplier: multiplier,
relation: .equal)
}
@discardableResult
public func jh_constrainAttribute(_ attribute: NSLayoutConstraint.Attribute,
toAttribute: NSLayoutConstraint.Attribute,
of otherView: UIView,
multiplier: CGFloat,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
jh_constrain(attribute: attribute,
toAttribute: toAttribute,
otherView: otherView,
multiplier: multiplier,
constant: 0.0,
relation: relation)
}
@discardableResult
public func jh_constraint(_ attribute: NSLayoutConstraint.Attribute,
toAttribute: NSLayoutConstraint.Attribute,
otherView: UIView?,
offset: CGFloat,
multiplier: CGFloat,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
jh_constrain(attribute: attribute, toAttribute: toAttribute, otherView: otherView, multiplier: multiplier, constant: offset, relation: relation)
}
}
// MARK: - Constraint
extension UIView {
///
public var jh_lastConstraint: NSLayoutConstraint? {
set {
objc_setAssociatedObject(self, &Key.lastConstraint, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
get {
objc_getAssociatedObject(self, &Key.lastConstraint) as? NSLayoutConstraint
}
}
public func jh_constraint(_ attribute: NSLayoutConstraint.Attribute,
toAttribute: NSLayoutConstraint.Attribute,
otherView: UIView?,
multiplier: CGFloat = 1.0,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint? {
let layoutKey = "\(attribute.rawValue)-\(relation.rawValue)-\((otherView?.hash ?? 0))-\(toAttribute.rawValue)-\(multiplier)"
return jh_layoutConstraints[layoutKey]
}
}
// MARK: - All
extension UIView {
///
public func jh_removeAllConstraints() {
NSLayoutConstraint.deactivate(jh_allConstraints)
jh_layoutConstraints.removeAll()
translatesAutoresizingMaskIntoConstraints = true
jh_lastConstraint = nil
}
}
// MARK: - Compression
extension UIView {
///
public var jh_compressionHorizontal: UILayoutPriority {
get {
contentCompressionResistancePriority(for: .horizontal)
}
set {
setContentCompressionResistancePriority(newValue, for: .horizontal)
}
}
///
public var jh_compressionVertical: UILayoutPriority {
get {
contentCompressionResistancePriority(for: .vertical)
}
set {
setContentCompressionResistancePriority(newValue, for: .vertical)
}
}
///
public var jh_huggingHorizontal: UILayoutPriority {
get {
contentHuggingPriority(for: .horizontal)
}
set {
setContentHuggingPriority(newValue, for: .horizontal)
}
}
///
public var jh_huggingVertical: UILayoutPriority {
get {
contentHuggingPriority(for: .vertical)
}
set {
setContentHuggingPriority(newValue, for: .vertical)
}
}
}
// MARK: - Collapse
extension UIView {
/// falsetrue0false
public var jh_collapsed: Bool {
set {
jh_allConstraints.forEach {
if newValue {
$0.constant = 0
} else {
$0.constant = $0.jh_originalConstraint
}
}
objc_setAssociatedObject(self, &Key.collapsed, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
get {
objc_getAssociatedObject(self, &Key.collapsed) as? Bool ?? false
}
}
/// imageniltextnil@""false
public var jh_autoCollapse: Bool {
set {
objc_setAssociatedObject(self, &Key.autoCollapse, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
get {
objc_getAssociatedObject(self, &Key.autoCollapse) as? Bool ?? false
}
}
/// false
public var jh_hiddenCollapse: Bool {
set {
objc_setAssociatedObject(self, &Key.hiddenCollapse, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
get {
objc_getAssociatedObject(self, &Key.hiddenCollapse) as? Bool ?? false
}
}
}
// MARK: - Axis
extension UIView {
@discardableResult
public func jh_alignCenterToSuperView(with offset: CGPoint = .zero) -> [NSLayoutConstraint] {
var constrints = [NSLayoutConstraint]()
constrints.append(jh_alignAxisToSuperView(.centerX, withOffset: offset.x))
constrints.append(jh_alignAxisToSuperView(.centerY, withOffset: offset.y))
return constrints
}
@discardableResult
public func jh_alignAxisToSuperView(_ axis: NSLayoutConstraint.Attribute,
withOffset: CGFloat = 0) -> NSLayoutConstraint {
jh_constrain(attribute: axis, toSuperView: self.superview, constant: withOffset, relation: .equal)
}
@discardableResult
public func jh_alignAxis(_ axis: NSLayoutConstraint.Attribute,
to otherView: UIView,
offset: CGFloat = 0,
multiplier: CGFloat = 1.0) -> NSLayoutConstraint {
jh_constrain(attribute: axis, toAttribute: axis, otherView: otherView, multiplier: multiplier, constant: offset, relation: .equal)
}
}
// MARK: - Edge
extension UIView {
@discardableResult
public func jh_pinEdgesToSuperView() -> [NSLayoutConstraint] {
jh_pinEdgesToSuperView(.zero)
}
@discardableResult
public func jh_pinEdgesToSuperView(_ insets: UIEdgeInsets) -> [NSLayoutConstraint] {
var constraints = [NSLayoutConstraint]()
constraints.append(jh_pinEdgeToSuperView(.top, inset: insets.top))
constraints.append(jh_pinEdgeToSuperView(.bottom, inset: insets.bottom))
constraints.append(jh_pinEdgeToSuperView(.leading, inset: insets.left))
constraints.append(jh_pinEdgeToSuperView(.trailing, inset: insets.right))
return constraints
}
@discardableResult
public func jh_pinEdgesToSuperView(_ insets: UIEdgeInsets,
excludingEdge: NSLayoutConstraint.Attribute) -> [NSLayoutConstraint] {
var constraints = [NSLayoutConstraint]()
if excludingEdge != .top {
constraints.append(jh_pinEdgeToSuperView(.top, inset: insets.top))
}
if excludingEdge != .bottom {
constraints.append(jh_pinEdgeToSuperView(.bottom, inset: insets.bottom))
}
if excludingEdge != .leading && excludingEdge != .left {
constraints.append(jh_pinEdgeToSuperView(.leading, inset: insets.left))
}
if excludingEdge != .trailing && excludingEdge != .right {
constraints.append(jh_pinEdgeToSuperView(.trailing, inset: insets.right))
}
return constraints
}
@discardableResult
public func jh_pinEdgesToSuperViewHorizontal(inset: CGFloat = 0) -> [NSLayoutConstraint] {
var constraints = [NSLayoutConstraint]()
constraints.append(jh_pinEdgeToSuperView(.leading, inset: inset))
constraints.append(jh_pinEdgeToSuperView(.trailing, inset: inset))
return constraints
}
@discardableResult
public func jh_pinEdgesToSuperViewVertical(inset: CGFloat = 0) -> [NSLayoutConstraint] {
var constraints = [NSLayoutConstraint]()
constraints.append(jh_pinEdgeToSuperView(.top, inset: inset))
constraints.append(jh_pinEdgeToSuperView(.bottom, inset: inset))
return constraints
}
@discardableResult
public func jh_pinEdgeToSuperView(_ edge: NSLayoutConstraint.Attribute) -> NSLayoutConstraint {
jh_pinEdgeToSuperView(edge,
inset: 0.0)
}
@discardableResult
public func jh_pinEdgeToSuperView(_ edge: NSLayoutConstraint.Attribute, inset: CGFloat) -> NSLayoutConstraint {
jh_pinEdgeToSuperView(edge,
inset: inset,
relation: .equal)
}
@discardableResult
public func jh_pinEdgeToSuperView(_ edge: NSLayoutConstraint.Attribute,
inset: CGFloat,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
jh_constrain(attribute: edge,
toSuperView: self.superview,
constant: inset,
relation: relation)
}
@discardableResult
public func jh_pinEdge(_ edge: NSLayoutConstraint.Attribute,
toEdge: NSLayoutConstraint.Attribute,
of otherView: UIView) -> NSLayoutConstraint {
jh_pinEdge(edge,
toEdge: toEdge,
of: otherView,
offset: 0.0)
}
@discardableResult
public func jh_pinEdge(_ edge: NSLayoutConstraint.Attribute,
toEdge: NSLayoutConstraint.Attribute,
of otherView: UIView,
offset: CGFloat) -> NSLayoutConstraint {
jh_pinEdge(edge,
toEdge: toEdge,
of: otherView,
offset: offset,
relation: .equal)
}
@discardableResult
public func jh_pinEdge(_ edge: NSLayoutConstraint.Attribute,
toEdge: NSLayoutConstraint.Attribute,
of otherView: UIView,
offset: CGFloat,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
jh_constrainAttribute(edge,
toAttribute: toEdge,
of: otherView,
offset: offset,
relation: relation)
}
}
// MARK: - Dimension
extension UIView {
@discardableResult
public func jh_setDimensionToSize(_ size: CGSize) -> [NSLayoutConstraint] {
var constraints = [NSLayoutConstraint]()
constraints.append(jh_setDimension(.width, toSize: size.width))
constraints.append(jh_setDimension(.height, toSize: size.height))
return constraints
}
@discardableResult
public func jh_setDimension(_ dimension: NSLayoutConstraint.Attribute,
toSize: CGFloat = 0,
relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
jh_constrain(attribute: dimension, toAttribute: .notAnAttribute, otherView: nil, multiplier: 1.0, constant: toSize, relation: relation)
}
@discardableResult
public func jh_matchDimension(_ dimension: NSLayoutConstraint.Attribute,
toDimension: NSLayoutConstraint.Attribute,
ofView otherView: UIView,
offset: CGFloat = 0,
relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
jh_constrainAttribute(dimension, toAttribute: toDimension, of: otherView, offset: offset, relation: relation)
}
@discardableResult
public func jh_matchDimension(_ dimension: NSLayoutConstraint.Attribute,
toDimension: NSLayoutConstraint.Attribute,
multiplier: CGFloat = 1.0,
relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
jh_constrainAttribute(dimension, toAttribute: toDimension, of: self, multiplier: multiplier, relation: relation)
}
@discardableResult
public func jh_matchDimension(_ dimension: NSLayoutConstraint.Attribute,
toDimension: NSLayoutConstraint.Attribute,
ofView otherView: UIView,
multiplier: CGFloat,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
jh_constrainAttribute(dimension, toAttribute: toDimension, of: otherView, multiplier: multiplier, relation: relation)
}
@discardableResult
public func jh_matchDimension(_ dimension: NSLayoutConstraint.Attribute,
toDimension: NSLayoutConstraint.Attribute,
ofView otherView: UIView,
offset: CGFloat,
multiplier: CGFloat,
relation: NSLayoutConstraint.Relation) -> NSLayoutConstraint {
jh_constrain(attribute: dimension, toAttribute: toDimension, otherView: otherView, multiplier: multiplier, constant: offset, relation: relation)
}
}
// MARK: - LayoutChain
extension UIView {
public var layoutChain: AutoLayoutSwift {
get {
guard let layout = objc_getAssociatedObject(self, &Key.jhLayoutChain) as? AutoLayoutSwift else {
let layout = AutoLayoutSwift(view: self)
objc_setAssociatedObject(self, &Key.jhLayoutChain, layout, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return layout
}
return layout
}
}
}