This commit is contained in:
DDIsFriend
2023-08-18 17:28:57 +08:00
commit f0e8a1709d
4282 changed files with 192396 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
//
// QLCompatibility.swift
// Pods
//
// Created by Daniel Huri on 5/12/18.
//
import Foundation
import UIKit
public typealias QLAttribute = NSLayoutConstraint.Attribute
public typealias QLRelation = NSLayoutConstraint.Relation
public typealias QLView = UIView
public typealias QLPriority = UILayoutPriority

View File

@@ -0,0 +1,107 @@
//
// QLUtils.swift
// QuickLayout
//
// Created by Daniel Huri on 11/21/17.
//
import Foundation
import UIKit
/**
Typealias for dictionary that contains multiple constraints
*/
public typealias QLMultipleConstraints = [QLAttribute: NSLayoutConstraint]
/**
Extends layout priority to other readable types
*/
public extension QLPriority {
static let must = QLPriority(rawValue: 999)
static let zero = QLPriority(rawValue: 0)
}
/**
Represents pair of attributes
*/
public struct QLAttributePair {
public let first: QLAttribute
public let second: QLAttribute
}
/**
Represents size constraints
*/
public struct QLSizeConstraints {
public let width: NSLayoutConstraint
public let height: NSLayoutConstraint
}
/**
Represents center constraints
*/
public struct QLCenterConstraints {
public let x: NSLayoutConstraint
public let y: NSLayoutConstraint
}
/**
Represents axis constraints (might be .top and .bottom, .left and .right, .leading and .trailing)
*/
public struct QLAxisConstraints {
public let first: NSLayoutConstraint
public let second: NSLayoutConstraint
}
/**
Represents center and size constraints
*/
public struct QLFillConstraints {
public let center: QLCenterConstraints
public let size: QLSizeConstraints
}
/**
Represents pair of priorities
*/
public struct QLPriorityPair {
public let horizontal: QLPriority
public let vertical: QLPriority
public static var required: QLPriorityPair {
return QLPriorityPair(.required, .required)
}
public static var must: QLPriorityPair {
return QLPriorityPair(.must, .must)
}
public init(_ horizontal: QLPriority, _ vertical: QLPriority) {
self.horizontal = horizontal
self.vertical = vertical
}
}
/**
Represents axis description
*/
public enum QLAxis {
case horizontally
case vertically
public var attributes: QLAttributePair {
let first: QLAttribute
let second: QLAttribute
switch self {
case .horizontally:
first = .left
second = .right
case .vertically:
first = .top
second = .bottom
}
return QLAttributePair(first: first, second: second)
}
}

View File

@@ -0,0 +1,111 @@
//
// QLView+QLContentWrap.swift
// QuickLayout
//
// Created by Daniel Huri on 11/21/17.
//
import Foundation
import UIKit
// MARK: Content Compression Resistance & Content Hugging Priority
public extension QLView {
/**
Force hugging and compression resistance for the given axes, using variadic parameter.
- parameter axes: The axes
*/
func forceContentWrap(_ axes: QLAxis...) {
if axes.contains(.vertically) {
verticalHuggingPriority = .required
verticalCompressionResistancePriority = .required
}
if axes.contains(.horizontally) {
horizontalHuggingPriority = .required
horizontalCompressionResistancePriority = .required
}
}
/**
Force hugging and compression resistance vertically and horizontally.
*/
func forceContentWrap() {
contentHuggingPriority = .required
contentCompressionResistancePriority = .required
}
/**
Vertical hugging priority
*/
var verticalHuggingPriority: QLPriority {
set {
setContentHuggingPriority(newValue, for: .vertical)
}
get {
return contentHuggingPriority(for: .vertical)
}
}
/**
Horizontal hugging priority
*/
var horizontalHuggingPriority: QLPriority {
set {
setContentHuggingPriority(newValue, for: .horizontal)
}
get {
return contentHuggingPriority(for: .horizontal)
}
}
/**
Content hugging priority (Vertical & Horizontal)
*/
var contentHuggingPriority: QLPriorityPair {
set {
horizontalHuggingPriority = newValue.horizontal
verticalHuggingPriority = newValue.vertical
}
get {
return QLPriorityPair(horizontalHuggingPriority, verticalHuggingPriority)
}
}
/**
Vertical content compression resistance priority
*/
var verticalCompressionResistancePriority: QLPriority {
set {
setContentCompressionResistancePriority(newValue, for: .vertical)
}
get {
return contentCompressionResistancePriority(for: .vertical)
}
}
/**
Horizontal content compression resistance priority
*/
var horizontalCompressionResistancePriority: QLPriority {
set {
setContentCompressionResistancePriority(newValue, for: .horizontal)
}
get {
return contentCompressionResistancePriority(for: .horizontal)
}
}
/**
Content compression resistance priority (Vertical & Horizontal)
*/
var contentCompressionResistancePriority: QLPriorityPair {
set {
horizontalCompressionResistancePriority = newValue.horizontal
verticalCompressionResistancePriority = newValue.vertical
}
get {
return QLPriorityPair(horizontalCompressionResistancePriority, verticalCompressionResistancePriority)
}
}
}

View File

@@ -0,0 +1,267 @@
//
// QLView+QuickLayout.swift
// QuickLayout
//
// Created by Daniel Huri on 11/19/17.
//
import Foundation
import UIKit
public extension QLView {
/**
Set constant value of an edge.
Should be used with *width* or *height*
- parameter edge: Edge type.
- parameter value: Edge size.
- parameter relation: Relation to the given constant value (default is *.equal*).
- parameter ratio: Ratio of the cconstant constraint to actual given value (default is *1*)
- parameter priority: Constraint's priority (default is *.required*).
- returns: The applied constraint (discardable).
*/
@discardableResult
func set(_ edge: QLAttribute, of value: CGFloat, relation: QLRelation = .equal,
ratio: CGFloat = 1.0, priority: QLPriority = .required) -> NSLayoutConstraint {
if translatesAutoresizingMaskIntoConstraints {
translatesAutoresizingMaskIntoConstraints = false
}
let constraint = NSLayoutConstraint(item: self, attribute: edge, relatedBy: relation, toItem: nil, attribute: .notAnAttribute, multiplier: ratio, constant: value)
constraint.priority = priority
addConstraint(constraint)
return constraint
}
/**
Set constant value for multiple edges simultaniously, using variadic parameter.
Should be used with *width* or *height*
- parameter edges: Edge types.
- parameter value: Edges size.
- parameter priority: Constraint's priority (default is *.required*).
- returns: The applied constraints in QLMultipleConstraints - see definition (discardable).
*/
@discardableResult
func set(_ edges: QLAttribute..., of value: CGFloat, relation: QLRelation = .equal,
ratio: CGFloat = 1.0, priority: QLPriority = .required) -> QLMultipleConstraints {
return set(edges, to: value, relation: relation, ratio: ratio, priority: priority)
}
/** **PRIVATELY USED** AS A REPLACEMENT for the variadic version for the method*/
@discardableResult
func set(_ edges: [QLAttribute], to value: CGFloat, relation: QLRelation = .equal,
ratio: CGFloat = 1.0, priority: QLPriority = .required) -> QLMultipleConstraints {
var constraints: QLMultipleConstraints = [:]
let uniqueEdges = Set(edges)
for edge in uniqueEdges {
let constraint = set(edge, of: value, priority: priority)
constraints[edge] = constraint
}
return constraints
}
/**
Layout edge to another view's edge.
- You can optionally define relation, ratio, constant and priority (each gets a default value)
- For example - Can be used to align self *left* edge to the *right* of another view.
- *self* and *view* must be directly connected (siblings / child-parent) in the view hierarchy.
- *superview* must not be *nil*.
- parameter edge: The edge of the first view. If not sent or *nil* - The function automatically assumes *edge* to be *otherEdge*
- parameter otherEdge: The edge of the second view.
- parameter view: The second view that self must be aligned with.
- parameter relation: The relation of the first edge to the second edge (default is .equal)
- parameter ratio: The ratio of the edge in relative to the superview edge (default is 1).
- parameter offset: Additional offset which is applied to the constraint (default is 0).
- parameter priority: Constraint's priority (default is *.required*).
- returns: The instance of the constraint that was applied (discardable). nil if method failed to apply the constraint.
*/
@discardableResult
func layout(_ edge: QLAttribute? = nil, to otherEdge: QLAttribute, of view: QLView,
relation: QLRelation = .equal, ratio: CGFloat = 1.0, offset: CGFloat = 0,
priority: QLPriority = .required) -> NSLayoutConstraint? {
guard isValidForQuickLayout else {
print("\(String(describing: self)) Error in func: \(#function)")
return nil
}
let constraint = NSLayoutConstraint(item: self, attribute: edge ?? otherEdge, relatedBy: relation, toItem: view, attribute: otherEdge, multiplier: ratio, constant: offset)
constraint.priority = priority
superview!.addConstraint(constraint)
return constraint
}
/**
Layout multiple edges of the view to the corresonding edges of another given view.
- You can optionally define relation, ratio, constant and priority (each gets a default value)
- For example - Can be used to align self *left* and *right* edges the same edge of another given view.
- *self* and *view* must be directly connected (siblings / child-parent) in the view hierarchy.
- *superview* must not be *nil*.
- parameter edges: The view edges
- parameter view: Another view that self must be aligned with.
- parameter relation: The relation of the edges. Can be applied to *.width* or *height* for example. (default is *.equal*).
- parameter ratio: The ratio of the edges to the other view edges (default is 1).
- parameter offset: Additional offset which is applied to each of the constraints (default is 0).
- parameter priority: Constraints' priority (default is *.required*).
- returns: The instance of the constraint that was applied (discardable). *nil* if the method failed to apply the constraint.
*/
@discardableResult
func layout(_ edges: QLAttribute..., to view: QLView, relation: QLRelation = .equal,
ratio: CGFloat = 1.0, offset: CGFloat = 0,
priority: QLPriority = .required) -> QLMultipleConstraints {
var constraints: QLMultipleConstraints = [:]
guard isValidForQuickLayout else {
print("\(String(describing: self)) Error in func: \(#function)")
return constraints
}
let uniqueEdges = Set(edges)
for edge in uniqueEdges {
let constraint = NSLayoutConstraint(item: self, attribute: edge, relatedBy: relation, toItem: view, attribute: edge, multiplier: ratio, constant: offset)
constraint.priority = priority
superview!.addConstraint(constraint)
constraints[edge] = constraint
}
return constraints
}
/**
Layout edge to the same edge of superview.
- Example of usage: *view.layoutToSuperview(.top)* makes *view* cling to the *top* of it's *superview*.
- You can optionally define ratio, constant and priority (each gets a default value)
- *superview* must not be *nil*.
- parameter edge: The edge (.width, .height, .left, .right, .leading, .trailing, etc...)
- parameter relation: The relation of the edge to the superview's corresponding edge (default is *.equal*)
- parameter ratio: The ratio of the edge in relative to the superview edge (default is 1).
- parameter offset: Additional offset from that can be applied to the constraint (default is 0).
- parameter priority: Constraint's priority (default is *.required*).
- returns: The instance of the constraint that was applied (discardable). Nil if method failed to apply constraint.
*/
@discardableResult
func layoutToSuperview(_ edge: QLAttribute, relation: QLRelation = .equal,
ratio: CGFloat = 1, offset: CGFloat = 0,
priority: QLPriority = .required) -> NSLayoutConstraint? {
guard isValidForQuickLayout else {
print("\(String(describing: self)) Error in func: \(#function)")
return nil
}
let constraint = NSLayoutConstraint(item: self, attribute: edge, relatedBy: relation, toItem: superview, attribute: edge, multiplier: ratio, constant: offset)
constraint.priority = priority
superview!.addConstraint(constraint)
return constraint
}
/**
Layout multiple edges to the same edges as superview, using variadic parameter.
Example for edges value:
- You can optionally define ratio, constant and priority (each gets a default value)
- *superview* must not be *nil*.
- parameter edges: The edges (.width, .height, .left, .right, .leading, .trailing, etc...)
- parameter relation: The relation of the edges to the superview's corresponding edges (default is *.equal*)
- parameter ratio: The ratio of the edges in relative to the superview edge (default is 1).
- parameter offset: Additional offset from that can be applied to the constraints (default is 0).
- parameter priority: Constraints' priority (default is *.required*).
- returns: The instance of QLMultipleConstraints - see type definition (discardable).
*/
@discardableResult
func layoutToSuperview(_ edges: QLAttribute..., relation: QLRelation = .equal,
ratio: CGFloat = 1, offset: CGFloat = 0,
priority: QLPriority = .required) -> QLMultipleConstraints {
var constraints: QLMultipleConstraints = [:]
guard !edges.isEmpty && isValidForQuickLayout else {
return constraints
}
let uniqueEdges = Set(edges)
for edge in uniqueEdges {
let constraint = NSLayoutConstraint(item: self, attribute: edge, relatedBy: relation, toItem: superview, attribute: edge, multiplier: ratio, constant: offset)
constraint.priority = priority
superview!.addConstraint(constraint)
constraints[edge] = constraint
}
return constraints
}
/**
Layout to one of the superview's axes.
- You can optionally define ratio, constant and priority (each gets a default value)
- *superview* must not be *nil*.
- parameter axis: The axis to which the view must be stretched (horizontally or vertically)
- parameter offset: Represents an additional edge offset from that can be applied to the constraints (default is 0)
- parameter priority: Represents constraint's priority (default is *.required*)
- returns: The instance of the constraint that was applied (discardable).
*/
@discardableResult
func layoutToSuperview(axis: QLAxis, offset: CGFloat = 0,
priority: QLPriority = .required) -> QLAxisConstraints? {
let attributes = axis.attributes
guard let first = layoutToSuperview(attributes.first, offset: offset, priority: priority) else {
return nil
}
guard let second = layoutToSuperview(attributes.second, offset: -offset, priority: priority) else {
return nil
}
return QLAxisConstraints(first: first, second: second)
}
/**
Size to superview with a given ratio and constant
- *superview* must not be *nil*.
- parameter ratio: The ratio of view to the size of superview.
- parameter offset: Represents an additional edge offset from that can be applied to the size (default is 0)
- parameter priority: Represents constraint's priority (default is *.required*)
- returns: The instance of QLSizeConstraints - see definition (discardable).
*/
@discardableResult
func sizeToSuperview(withRatio ratio: CGFloat = 1, offset: CGFloat = 0,
priority: QLPriority = .required) -> QLSizeConstraints? {
let size = layoutToSuperview(.width, .height, ratio: ratio, offset: offset, priority: priority)
guard !size.isEmpty else {
return nil
}
return QLSizeConstraints(width: size[.width]!, height: size[.height]!)
}
/**
Center in superview with an optional offset
- *superview* must not be *nil*.
- parameter offset: Represents an additional offset from the center (default is 0)
- parameter priority: Represents constraint's priority (default is *.required*)
- returns: The instance of QLCenterConstraints - see definition (discardable).
*/
@discardableResult
func centerInSuperview(offset: CGFloat = 0, priority: QLPriority = .required) -> QLCenterConstraints? {
let center = layoutToSuperview(.centerX, .centerY, offset: offset)
guard !center.isEmpty else {
return nil
}
return QLCenterConstraints(x: center[.centerX]!, y: center[.centerY]!)
}
/**
Fill superview totally (center and size to superview)
- *superview* must not be *nil*.
- parameter ratio: Ratio to the superview's size (default is 1)
- parameter offset: Offset from center (default is 0)
- parameter priority: Represents constraint's priority (default is *.required*)
- returns: The instance of QLFillConstraints - see definition (discardable).
*/
@discardableResult
func fillSuperview(withSizeRatio ratio: CGFloat = 1, offset: CGFloat = 0,
priority: QLPriority = .required) -> QLFillConstraints? {
guard let center = centerInSuperview(priority: priority) else {
return nil
}
guard let size = sizeToSuperview(withRatio: ratio, offset: offset, priority: priority) else {
return nil
}
return QLFillConstraints(center: center, size: size)
}
/** **PRIVATELY USED** to test for validation*/
var isValidForQuickLayout: Bool {
guard superview != nil else {
print("\(String(describing: self)):\(#function) - superview is unexpectedly nullified")
return false
}
if translatesAutoresizingMaskIntoConstraints {
translatesAutoresizingMaskIntoConstraints = false
}
return true
}
}

View File

@@ -0,0 +1,214 @@
//
// QLViewArray+QuickLayout.swift
// QuickLayout
//
// Created by Daniel Huri on 11/20/17.
//
import Foundation
import UIKit
// MARK: Multiple Views in Array
public extension Array where Element: QLView {
/**
All elements in the collection recieve constant value for the given edge.
- parameter edge: Should be used with *.width* or *.height*.
- parameter value: The size of the edge.
- parameter priority: Constraint's priority (default is *.required*).
- returns: The instance of the constraint that was applied (discardable).
*/
@discardableResult
func set(_ edge: QLAttribute, of value: CGFloat,
priority: QLPriority = .required) -> [NSLayoutConstraint] {
var constraints: [NSLayoutConstraint] = []
for view in self {
let constraint = view.set(edge, of: value)
constraints.append(constraint)
}
return constraints
}
/**
All elements in the collection recieve constant value for the given edges, using variadic parameter.
- parameter edges: Should be used with *.width* or *.height*.
- parameter value: The size of the edge.
- parameter priority: Constraint's priority (default is *.required*).
- returns: The instance of the constraint that was applied (discardable).
*/
@discardableResult
func set(_ edges: QLAttribute..., of value: CGFloat,
priority: QLPriority = .required) -> [QLMultipleConstraints] {
var constraintsArray: [QLMultipleConstraints] = []
for view in self {
let constraints = view.set(edges, to: value, priority: priority)
constraintsArray.append(constraints)
}
return constraintsArray
}
/**
Spread elements consecutively according to the given axis.
- parameter axis: The axis: *.vertically*, *horizontally*
- parameter stretchEdgesToSuperview: Decides whether the first and last items in the array must be clipped to their parent edges.
- parameter priority: Constraint's priority (default is *.required*).
- returns: Array of constraints that were applied (discardable)
*/
@discardableResult
func spread(_ axis: QLAxis, stretchEdgesToSuperview: Bool = false, offset: CGFloat = 0,
priority: QLPriority = .required) -> [NSLayoutConstraint] {
guard isValidForQuickLayout else {
return []
}
let attributes = axis.attributes
var constraints: [NSLayoutConstraint] = []
if stretchEdgesToSuperview {
let constraint = first!.layoutToSuperview(attributes.first, offset: offset)!
constraints.append(constraint)
}
for (index, view) in enumerated() {
guard index > 0 else {
continue
}
let previousView = self[index - 1]
let constraint = view.layout(attributes.first, to: attributes.second, of: previousView, offset: offset, priority: priority)!
constraints.append(constraint)
}
if stretchEdgesToSuperview {
let constraint = last!.layoutToSuperview(attributes.second, offset: -offset)!
constraints.append(constraint)
}
return constraints
}
/**
Layout elements to superview's axis
- parameter axis: The axis: *.vertically*, *horizontally*
- parameter offset: Additional side offset that must be applied (identical spacing from each side)
- parameter priority: Constraint's priority (default is *.required*).
- returns: Array of QLAxisConstraints - see definition (discardable)
*/
@discardableResult
func layoutToSuperview(axis: QLAxis, offset: CGFloat = 0,
priority: QLPriority = .required) -> [QLAxisConstraints] {
let attributes = axis.attributes
let firstConstraints = layoutToSuperview(attributes.first, offset: offset, priority: priority)
guard !firstConstraints.isEmpty else {
return []
}
let secondConstraints = layoutToSuperview(attributes.second, offset: -offset, priority: priority)
guard !secondConstraints.isEmpty else {
return []
}
var constraints: [QLAxisConstraints] = []
for (first, second) in zip(firstConstraints, secondConstraints) {
constraints.append(QLAxisConstraints(first: first, second: second))
}
return constraints
}
/**
Layout elements' edges to superview's edge (The same edge - top to top, bottom to bottom, etc...)
- parameter edge: The edge of the view / superview
- parameter ratio: The ratio of the edge in relation to the superview's (default is 1).
- parameter offset: Additional offset from that must be applied to the constraint (default is 0).
- parameter priority: Constraint's priority (default is *.required*).
- returns: Array of applied constraints - see definition (discardable)
*/
@discardableResult
func layoutToSuperview(_ edge: QLAttribute, ratio: CGFloat = 1, offset: CGFloat = 0,
priority: QLPriority = .required) -> [NSLayoutConstraint] {
guard isValidForQuickLayout else {
return []
}
return layout(to: edge, of: first!.superview!, ratio: ratio, offset: offset, priority: priority)
}
/**
Layout elements' edges to to anchorView edge
- parameter firstEdge: The edge of the elements in the array
- parameter anchorEdge: The edge of the anchor view
- parameter anchorView: The anchor view
- parameter ratio: The ratio of the edge in relative to the superview edge (default is 1).
- parameter offset: Additional offset from that can be applied to the constraints (default is 0).
- parameter priority: Constraints' priority (default is *.required*).
- returns: Array of applied constraints - see definition (discardable)
*/
@discardableResult
func layout(_ firstEdge: QLAttribute? = nil, to anchorEdge: QLAttribute,
of anchorView: QLView, ratio: CGFloat = 1, offset: CGFloat = 0,
priority: QLPriority = .required) -> [NSLayoutConstraint] {
guard isValidForQuickLayout else {
return []
}
let edge: QLAttribute
if let firstEdge = firstEdge {
edge = firstEdge
} else {
edge = anchorEdge
}
var result: [NSLayoutConstraint] = []
for view in self {
let constraint = view.layout(edge, to: anchorEdge, of: anchorView, ratio: ratio, offset: offset, priority: priority)!
result.append(constraint)
}
return result
}
/**
Layout elements' multiple edges to to anchorView's same edges (top to top, bottom to bottom, etc...)
- parameter edges: The edges of the view - variadic parameter
- parameter anchorView: The anchor view
- parameter ratio: The ratio of the edge in relative to the superview edge (default is 1).
- parameter offset: Additional offset from that can be applied to the constraints (default is 0).
- parameter priority: Constraints' priority (default is *.required*).
- returns: Array of applied constraints, each element is of type QLMultipleConstraints - see definition (discardable)
*/
@discardableResult
func layout(_ edges: QLAttribute..., to anchorView: QLView,
ratio: CGFloat = 1, offset: CGFloat = 0,
priority: QLPriority = .required) -> [QLMultipleConstraints] {
guard !edges.isEmpty && isValidForQuickLayout else {
return []
}
// Avoid duplicities
let uniqueEdges = Set(edges)
var result: [QLMultipleConstraints] = []
for view in self {
var multipleConstraints: QLMultipleConstraints = [:]
for edge in uniqueEdges {
let constraint = view.layout(to: edge, of: anchorView, ratio: ratio, offset: offset, priority: priority)!
multipleConstraints[edge] = constraint
}
result.append(multipleConstraints)
}
return result
}
/** **PRIVATELY USED** to test for validation*/
var isValidForQuickLayout: Bool {
guard !isEmpty else {
print("\(String(describing: self)) Error in func: \(#function), Views collection is empty!")
return false
}
for view in self {
guard view.isValidForQuickLayout else {
print("\(String(describing: self)) Error in func: \(#function)")
return false
}
}
return true
}
}