宽窄优行-由【嘉易行】项目成品而来
younger_times
2023-04-06 a1ae6802080a22e6e6ce6d0935e95facb1daca5c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//
//  QLViewArray+QuickLayout.swift
//  QuickLayout
//
//  Created by Daniel Huri on 11/20/17.
//
 
import Foundation
#if os(OSX)
import AppKit
#else
import UIKit
#endif
 
// 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
    }
}