-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathSTIBInspectable.swift
More file actions
288 lines (253 loc) · 8.83 KB
/
Copy pathSTIBInspectable.swift
File metadata and controls
288 lines (253 loc) · 8.83 KB
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
//
// STIBInspectable.swift
// STBaseProject
//
// Created by 寒江孤影 on 2017/02/24.
//
import UIKit
// MARK: - 约束适配类型
public enum STConstraintAdaptType {
case width // 用于宽度约束
case height // 高用于高度约束
case both // 宽高都适配,通常用于尺寸约束
case spacing // 间距适配 - 用于元素之间的距离
case margin // 边距适配 - 用于视图与父视图边缘的距离
case fontSize // 字体大小适配
case custom(CGFloat) // 自定义比例适配
}
extension NSLayoutConstraint {
private enum AssociatedKeys {
static var autoConstantKey: UInt8 = 0
static var adaptTypeKey: UInt8 = 1
static var originalConstantKey: UInt8 = 2
static var isAdaptedKey: UInt8 = 3
}
/// 是否启用自动适配
private var _autoConstant: Bool {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.autoConstantKey) as? Bool ?? false
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.autoConstantKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/// 适配类型
private var _adaptType: STConstraintAdaptType {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.adaptTypeKey) as? STConstraintAdaptType ?? .both
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.adaptTypeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/// 原始约束值
private var _originalConstant: CGFloat {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.originalConstantKey) as? CGFloat ?? self.constant
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.originalConstantKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/// 是否已经适配过
private var _isAdapted: Bool {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.isAdaptedKey) as? Bool ?? false
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.isAdaptedKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
// MARK: - IBInspectable 属性
/// 是否启用自动适配(IBInspectable)
@IBInspectable open var autoConstant: Bool {
get {
return _autoConstant
}
set {
_autoConstant = newValue
if newValue && !_isAdapted {
_originalConstant = self.constant
self.adaptConstraintIfNeeded()
} else if !newValue && _isAdapted {
self.constant = _originalConstant
_isAdapted = false
}
}
}
/// 适配类型(IBInspectable)
@IBInspectable open var adaptType: Int {
get {
switch _adaptType {
case .width: return 0
case .height: return 1
case .both: return 2
case .spacing: return 3
case .margin: return 4
case .fontSize: return 5
case .custom: return 2
}
}
set {
let type: STConstraintAdaptType
switch newValue {
case 0: type = .width
case 1: type = .height
case 2: type = .both
case 3: type = .spacing
case 4: type = .margin
case 5: type = .fontSize
default: type = .both
}
_adaptType = type
if _autoConstant && !_isAdapted {
self.adaptConstraintIfNeeded()
}
}
}
/// 自定义适配比例(IBInspectable)
@IBInspectable open var customAdaptRatio: CGFloat {
get {
if case .custom(let ratio) = _adaptType {
return ratio
}
return 1.0
}
set {
_adaptType = .custom(newValue)
if _autoConstant && !_isAdapted {
self.adaptConstraintIfNeeded()
}
}
}
/// 执行约束适配
private func adaptConstraintIfNeeded() {
guard _autoConstant && !_isAdapted else { return }
let originalValue = _originalConstant
let adaptedValue: CGFloat
switch _adaptType {
case .width:
adaptedValue = STDeviceAdapter.scaledWidth(originalValue)
case .height:
adaptedValue = STDeviceAdapter.scaledHeight(originalValue)
case .both:
adaptedValue = STDeviceAdapter.scaledWidth(originalValue)
case .spacing:
adaptedValue = STDeviceAdapter.scaledSpacing(originalValue)
case .margin:
adaptedValue = STDeviceAdapter.scaledSpacing(originalValue)
case .fontSize:
adaptedValue = STDeviceAdapter.scaledFontSize(originalValue)
case .custom(let ratio):
adaptedValue = originalValue * ratio * STDeviceAdapter.widthScale
}
self.constant = adaptedValue
_isAdapted = true
}
/// 手动触发适配
public func applyAdaptiveConstant() {
if _autoConstant {
_isAdapted = false
self.adaptConstraintIfNeeded()
}
}
/// 重置为原始值
public func resetAdaptiveConstant() {
if _isAdapted {
self.constant = _originalConstant
_isAdapted = false
}
}
/// 获取原始约束值
public var originalConstant: CGFloat {
return _originalConstant
}
/// 获取适配后的约束值
public var adaptedConstant: CGFloat {
self.constant
}
/// 检查是否已适配
public var hasAdaptiveConstantApplied: Bool {
_isAdapted
}
// MARK: - 生命周期方法
override open func awakeFromNib() {
super.awakeFromNib()
if _autoConstant && !_isAdapted {
_originalConstant = self.constant
self.adaptConstraintIfNeeded()
}
}
}
// MARK: - 批量约束适配工具
public enum STConstraintAdapter {
/// 批量适配约束
/// - Parameter constraints: 约束数组
public static func adaptConstraints(_ constraints: [NSLayoutConstraint]) {
for constraint in constraints {
constraint.applyAdaptiveConstant()
}
}
/// 批量重置约束
/// - Parameter constraints: 约束数组
public static func resetConstraints(_ constraints: [NSLayoutConstraint]) {
for constraint in constraints {
constraint.resetAdaptiveConstant()
}
}
/// 获取所有已适配的约束
/// - Parameter constraints: 约束数组
/// - Returns: 已适配的约束数组
public static func adaptedConstraints(from constraints: [NSLayoutConstraint]) -> [NSLayoutConstraint] {
return constraints.filter(\.hasAdaptiveConstantApplied)
}
/// 获取所有未适配的约束
/// - Parameter constraints: 约束数组
/// - Returns: 未适配的约束数组
public static func unadaptedConstraints(from constraints: [NSLayoutConstraint]) -> [NSLayoutConstraint] {
return constraints.filter { !$0.hasAdaptiveConstantApplied }
}
}
// MARK: - UIView 约束适配扩展
public extension UIView {
func adaptAllConstraints() {
self.adaptConstraintsRecursively(in: self)
}
/// 适配约束
private func adaptConstraintsRecursively(in view: UIView) {
for constraint in view.constraints {
constraint.applyAdaptiveConstant()
}
for subview in view.subviews {
self.adaptConstraintsRecursively(in: subview)
}
}
/// 重置所有子视图的约束
func resetAllConstraints() {
self.resetConstraintsRecursively(in: self)
}
/// 重置约束
private func resetConstraintsRecursively(in view: UIView) {
for constraint in view.constraints {
constraint.resetAdaptiveConstant()
}
for subview in view.subviews {
self.resetConstraintsRecursively(in: subview)
}
}
/// 获取所有已适配的约束
func allAdaptedConstraints() -> [NSLayoutConstraint] {
var adaptedConstraints: [NSLayoutConstraint] = []
self.collectAdaptedConstraintsRecursively(in: self, result: &adaptedConstraints)
return adaptedConstraints
}
/// 收集已适配的约束
private func collectAdaptedConstraintsRecursively(in view: UIView, result: inout [NSLayoutConstraint]) {
for constraint in view.constraints where constraint.hasAdaptiveConstantApplied {
result.append(constraint)
}
for subview in view.subviews {
self.collectAdaptedConstraintsRecursively(in: subview, result: &result)
}
}
}