Skip to content

Commit 886b3dd

Browse files
committed
Fix crossconstraints issue when views are added out of order
1 parent ccaca62 commit 886b3dd

File tree

2 files changed

+46
-34
lines changed

2 files changed

+46
-34
lines changed

Classes/Extensions/DeclarativeProtocol+Constraints.swift

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extension DeclarativeProtocol {
1010
return self
1111
}
1212

13+
@discardableResult
1314
public func size(_ value: ConstraintValue) -> Self {
1415
width(value)
1516
height(value)
@@ -27,15 +28,15 @@ extension DeclarativeProtocol {
2728
public func width(_ value: ConstraintValue) -> Self {
2829
let preConstraint = PreConstraint(attribute1: .width, attribute2: nil, value: value.constraintValue)
2930
_declarativeView._preConstraints.solo[.width] = preConstraint
30-
activateSolo(preConstraint: preConstraint, side: .width)
31+
declarativeView.activateSolo(preConstraint: preConstraint, side: .width)
3132
return self
3233
}
3334

3435
@discardableResult
3536
public func height(_ value: ConstraintValue) -> Self {
3637
let preConstraint = PreConstraint(attribute1: .height, attribute2: nil, value: value.constraintValue)
3738
_declarativeView._preConstraints.solo[.height] = preConstraint
38-
activateSolo(preConstraint: preConstraint, side: .height)
39+
declarativeView.activateSolo(preConstraint: preConstraint, side: .height)
3940
return self
4041
}
4142

@@ -44,6 +45,11 @@ extension DeclarativeProtocol {
4445
return dimension(.width, to: declarativeView, .height, value)
4546
}
4647

48+
@discardableResult
49+
public func aspectRatio(value: CGFloat = 0, multiplier: CGFloat = 1, priority: UILayoutPriority = .defaultHigh) -> Self {
50+
return dimension(.width, to: declarativeView, .height, ConstraintValueType(.equal, value, multiplier, priority))
51+
}
52+
4753
// MARK: - Edges
4854

4955
private func _edgeSuperview(anySide: DeclarativeConstraintAnySide, to view: UIView?, toAnySide: DeclarativeConstraintAnySide, _ value: ConstraintValue = CGFloat(0)) -> Self {
@@ -53,7 +59,7 @@ extension DeclarativeProtocol {
5359
if let view = view {
5460
preConstraint.setSide(with: anySide, to: view, toAnySide: toAnySide)
5561
if let _ = declarativeView.superview {
56-
activateSuper(anySide.attribute, to: view, side: toAnySide.attribute, preConstraint: preConstraint)
62+
declarativeView.activateSuper(anySide.attribute, to: view, side: toAnySide.attribute, preConstraint: preConstraint)
5763
}
5864
}
5965
_declarativeView._preConstraints.super[anySide.attribute] = preConstraint
@@ -113,7 +119,8 @@ extension DeclarativeProtocol {
113119

114120
@discardableResult
115121
private func preActivateRelative(_ preConstraint: PreConstraint, side1: NSLayoutConstraint.Attribute, side2: NSLayoutConstraint.Attribute, view: UIView) -> Self {
116-
return activateRelative(side1, to: view, side: side2, preConstraint: preConstraint)
122+
declarativeView.activateRelative(side1, to: view, side: side2, preConstraint: preConstraint)
123+
return self
117124
}
118125

119126
// MARK: - Superview
@@ -313,15 +320,18 @@ extension DeclarativeProtocol {
313320
}
314321

315322
public var outer: OuterConstraintValues { return .init(declarativeView, _declarativeView._constraintsOuter) }
316-
323+
}
324+
325+
extension UIView {
317326
// MARK: - Activation
318327

319328
func activateSolo(preConstraint: PreConstraint, side: NSLayoutConstraint.Attribute) {
329+
guard let self = self as? DeclarativeProtocolInternal else { return }
320330
let constant = preConstraint.value.value
321331
var constraint: NSLayoutConstraint?
322332
switch side {
323333
case .width, .height:
324-
constraint = .init(item: declarativeView,
334+
constraint = .init(item: self,
325335
attribute: side,
326336
relatedBy: preConstraint.value.relation,
327337
toItem: nil,
@@ -331,53 +341,55 @@ extension DeclarativeProtocol {
331341
default: return
332342
}
333343
if let constraint = constraint {
334-
if let _ = _declarativeView._constraintsMain[side] {
335-
_declarativeView._constraintsMain.setValue(constraint.constant, for: side)
344+
if let _ = self._constraintsMain[side] {
345+
self._constraintsMain.setValue(constraint.constant, for: side)
336346
} else {
337-
_declarativeView._constraintsMain[side] = constraint.update(preConstraint.value).activated()
347+
self._constraintsMain[side] = constraint.update(preConstraint.value).activated()
338348
}
339349
}
340350
}
341351

342352
func activateSuper(_ side: NSLayoutConstraint.Attribute, to: UIView, side toSide: NSLayoutConstraint.Attribute, preConstraint: PreConstraint) {
343-
_declarativeView._constraintsMain.removeValue(for: side)
344-
_declarativeView._preConstraints.super[side] = preConstraint
353+
guard let s = self as? DeclarativeProtocolInternal else { return }
354+
s._constraintsMain.removeValue(for: side)
355+
s._preConstraints.super[side] = preConstraint
345356
let constant = preConstraint.value.value
346-
let constraint = NSLayoutConstraint(item: declarativeView,
357+
let constraint = NSLayoutConstraint(item: self,
347358
attribute: side,
348359
relatedBy: preConstraint.value.relation,
349360
toItem: to,
350361
attribute: toSide,
351362
multiplier: preConstraint.value.multiplier,
352363
constant: constant)
353-
_declarativeView._constraintsMain.setValue(constraint.update(preConstraint.value), for: side)
364+
s._constraintsMain.setValue(constraint.update(preConstraint.value), for: side)
354365
if let dest = to as? DeclarativeProtocolInternal {
355-
dest._preConstraints.relative.setValue(side: side, value: preConstraint.value, forKey: toSide, andView: declarativeView)
356-
dest._constraintsOuter.setValue(constraint, forKey: toSide, andView: declarativeView)
366+
dest._preConstraints.relative.setValue(side: side, value: preConstraint.value, forKey: toSide, andView: self)
367+
dest._constraintsOuter.setValue(constraint, forKey: toSide, andView: self)
357368
}
358-
if declarativeView.superview != nil && to.superview != nil || declarativeView.superview == to || to.superview == declarativeView {
369+
if superview != nil && to.superview != nil || superview == to || to.superview == self {
359370
constraint.isActive = true
360371
}
361372
}
362373

363374
@discardableResult
364-
func activateRelative(_ side: NSLayoutConstraint.Attribute, to: UIView, side toSide: NSLayoutConstraint.Attribute, preConstraint: PreConstraint) -> Self {
365-
_declarativeView._constraintsOuter.removeValue(forKey: side, andView: to)
366-
_declarativeView._preConstraints.relative.setValue(side: side, value: preConstraint.value, forKey: toSide, andView: to)
375+
func activateRelative(_ side: NSLayoutConstraint.Attribute, to: UIView, side toSide: NSLayoutConstraint.Attribute, preConstraint: PreConstraint, second: Bool = false) -> Self {
376+
guard let s = self as? DeclarativeProtocolInternal else { return self }
377+
s._constraintsOuter.removeValue(forKey: side, andView: to)
378+
s._preConstraints.relative.setValue(side: side, value: preConstraint.value, forKey: toSide, andView: to)
367379
let constant = preConstraint.value.value
368-
let constraint = NSLayoutConstraint(item: declarativeView,
369-
attribute: side,
370-
relatedBy: preConstraint.value.relation,
371-
toItem: to,
372-
attribute: toSide,
373-
multiplier: preConstraint.value.multiplier,
374-
constant: constant)
375-
_declarativeView._constraintsOuter.setValue(constraint, forKey: side, andView: to)
376-
if let dest = to as? DeclarativeProtocolInternal {
377-
dest._preConstraints.relative.setValue(side: side, value: preConstraint.value, forKey: toSide, andView: declarativeView)
378-
dest._constraintsOuter.setValue(constraint, forKey: toSide, andView: declarativeView)
380+
let constraint = NSLayoutConstraint(item: self,
381+
attribute: side,
382+
relatedBy: preConstraint.value.relation,
383+
toItem: to,
384+
attribute: toSide,
385+
multiplier: preConstraint.value.multiplier,
386+
constant: constant)
387+
s._constraintsOuter.setValue(constraint, forKey: side, andView: to)
388+
if !second, to.superview == nil, var dest = to as? DeclarativeProtocolInternal {
389+
to.activateRelative(toSide, to: self, side: side, preConstraint: preConstraint, second: true)
379390
}
380-
if declarativeView.superview != nil && to.superview != nil || declarativeView.superview == to || to.superview == declarativeView {
391+
let isDescant = isDescendant(of: to.superview ?? UIView()) || to.isDescendant(of: superview ?? UIView())
392+
if (superview != nil && to.superview != nil && isDescant) || superview == to || to.superview == self {
381393
constraint.isActive = true
382394
}
383395
return self

Classes/Extensions/DeclarativeProtocol+MovedToSuperview.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ extension DeclarativeProtocol {
66
NSLayoutConstraint.Attribute.all.forEach { side in
77
if let solo = _declarativeView._preConstraints.solo[side] {
88
if _declarativeView._constraintsMain.isNotActive(side) {
9-
activateSolo(preConstraint: solo, side: side)
9+
declarativeView.activateSolo(preConstraint: solo, side: side)
1010
}
1111
} else if let `super` = _declarativeView._preConstraints.super[side] {
1212
if _declarativeView._constraintsMain.isNotActive(side) {
13-
activateSuper(side, to: superview, side: side, preConstraint: `super`)
13+
declarativeView.activateSuper(side, to: superview, side: side, preConstraint: `super`)
1414
}
1515
} else if let relative = _declarativeView._preConstraints.relative[side] {
1616
relative.forEach { view, constraintsKeyValue in
1717
constraintsKeyValue.forEach { key, value in
1818
if _declarativeView._constraintsOuter.isNotActive(key, view) {
19-
activateRelative(side, to: view, side: key, preConstraint: .init(attribute1: side, attribute2: key, value: value))
19+
declarativeView.activateRelative(side, to: view, side: key, preConstraint: .init(attribute1: side, attribute2: key, value: value))
2020
}
2121
}
2222
}

0 commit comments

Comments
 (0)