宽窄优行-由【嘉易行】项目成品而来
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
//
//  QLView+QuickLayout.swift
//  QuickLayout
//
//  Created by Daniel Huri on 11/19/17.
//
 
import Foundation
#if os(OSX)
import AppKit
#else
import UIKit
#endif
 
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
    }
}