forked from shadowfacts/Tusker
Remove ambiguating constraint priorities, avoid removing and recreating the same constraints
Closes #407
This commit is contained in:
parent
50bfaf7236
commit
3c9692d5b2
|
@ -48,11 +48,7 @@ class ActionNotificationGroupCollectionViewCell: UICollectionViewListCell {
|
|||
$0.axis = .horizontal
|
||||
$0.alignment = .fill
|
||||
$0.spacing = 8
|
||||
let heightConstraint = $0.heightAnchor.constraint(equalToConstant: 30)
|
||||
// the collection view cell imposes a height constraint before it's calculated the actual height
|
||||
// so let this constraint be broken temporarily to avoid unsatisfiable constraints log spam
|
||||
heightConstraint.priority = .init(999)
|
||||
heightConstraint.isActive = true
|
||||
$0.heightAnchor.constraint(equalToConstant: 30).isActive = true
|
||||
}
|
||||
|
||||
private lazy var actionLabel = MultiSourceEmojiLabel().configure {
|
||||
|
|
|
@ -45,9 +45,7 @@ class FollowNotificationGroupCollectionViewCell: UICollectionViewListCell {
|
|||
]).configure {
|
||||
$0.axis = .horizontal
|
||||
$0.alignment = .fill
|
||||
let heightConstraint = $0.heightAnchor.constraint(equalToConstant: 30)
|
||||
heightConstraint.priority = .init(999)
|
||||
heightConstraint.isActive = true
|
||||
$0.heightAnchor.constraint(equalToConstant: 30).isActive = true
|
||||
}
|
||||
|
||||
private lazy var actionLabel = MultiSourceEmojiLabel().configure {
|
||||
|
|
|
@ -48,9 +48,7 @@ class FollowRequestNotificationCollectionViewCell: UICollectionViewListCell {
|
|||
]).configure {
|
||||
$0.axis = .horizontal
|
||||
$0.alignment = .fill
|
||||
let heightConstraint = $0.heightAnchor.constraint(equalToConstant: 30)
|
||||
heightConstraint.priority = .init(999)
|
||||
heightConstraint.isActive = true
|
||||
$0.heightAnchor.constraint(equalToConstant: 30).isActive = true
|
||||
}
|
||||
|
||||
private lazy var actionLabel = EmojiLabel().configure {
|
||||
|
|
|
@ -91,9 +91,6 @@ class PollFinishedNotificationCollectionViewCell: UICollectionViewListCell {
|
|||
contentView.addSubview(iconView)
|
||||
vStack.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(vStack)
|
||||
let vStackBottomConstraint = vStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8)
|
||||
// need something to break during intermediate layouts when the cell imposes a 44pt height :S
|
||||
vStackBottomConstraint.priority = .init(999)
|
||||
NSLayoutConstraint.activate([
|
||||
iconView.topAnchor.constraint(equalTo: vStack.topAnchor),
|
||||
iconView.trailingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16 + 50),
|
||||
|
@ -101,7 +98,7 @@ class PollFinishedNotificationCollectionViewCell: UICollectionViewListCell {
|
|||
vStack.leadingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: 8),
|
||||
vStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
|
||||
vStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
|
||||
vStackBottomConstraint,
|
||||
vStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
|
||||
])
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
||||
|
|
|
@ -56,13 +56,8 @@ class PollOptionView: UIView {
|
|||
percentLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||
addSubview(percentLabel)
|
||||
|
||||
let minHeightConstraint = heightAnchor.constraint(greaterThanOrEqualToConstant: PollOptionView.minHeight)
|
||||
// on the first layout, something is weird and this becomes ambiguous even though it's fine on subsequent layouts
|
||||
// this keeps autolayout from complaining
|
||||
minHeightConstraint.priority = .required - 1
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
minHeightConstraint,
|
||||
heightAnchor.constraint(greaterThanOrEqualToConstant: PollOptionView.minHeight),
|
||||
|
||||
label.topAnchor.constraint(equalTo: topAnchor, constant: 4),
|
||||
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4),
|
||||
|
|
|
@ -50,8 +50,9 @@ class StatusContentContainer<ContentView: ContentTextView, PollView: StatusConte
|
|||
|
||||
private var isHiddenObservations: [NSKeyValueObservation] = []
|
||||
|
||||
private var visibleSubviews = IndexSet()
|
||||
private var verticalConstraints: [NSLayoutConstraint] = []
|
||||
private var lastSubviewBottomConstraint: NSLayoutConstraint?
|
||||
private var lastSubviewBottomConstraint: (UIView, NSLayoutConstraint)?
|
||||
private var zeroHeightConstraint: NSLayoutConstraint!
|
||||
|
||||
private var isCollapsed = false
|
||||
|
@ -93,31 +94,35 @@ class StatusContentContainer<ContentView: ContentTextView, PollView: StatusConte
|
|||
}
|
||||
|
||||
override func updateConstraints() {
|
||||
NSLayoutConstraint.deactivate(verticalConstraints)
|
||||
verticalConstraints = []
|
||||
var lastVisibleSubview: UIView?
|
||||
|
||||
for subview in arrangedSubviews {
|
||||
guard !subview.isHidden else {
|
||||
continue
|
||||
let visibleSubviews = IndexSet(arrangedSubviews.indices.filter { !arrangedSubviews[$0].isHidden })
|
||||
if self.visibleSubviews != visibleSubviews {
|
||||
self.visibleSubviews = visibleSubviews
|
||||
NSLayoutConstraint.deactivate(verticalConstraints)
|
||||
verticalConstraints = []
|
||||
var lastVisibleSubview: UIView?
|
||||
|
||||
for subviewIndex in visibleSubviews {
|
||||
let subview = arrangedSubviews[subviewIndex]
|
||||
if let lastVisibleSubview {
|
||||
verticalConstraints.append(subview.topAnchor.constraint(equalTo: lastVisibleSubview.bottomAnchor, constant: 4))
|
||||
} else {
|
||||
verticalConstraints.append(subview.topAnchor.constraint(equalTo: topAnchor))
|
||||
}
|
||||
lastVisibleSubview = subview
|
||||
}
|
||||
|
||||
if let lastVisibleSubview {
|
||||
verticalConstraints.append(subview.topAnchor.constraint(equalTo: lastVisibleSubview.bottomAnchor, constant: 4))
|
||||
} else {
|
||||
verticalConstraints.append(subview.topAnchor.constraint(equalTo: topAnchor))
|
||||
}
|
||||
|
||||
lastVisibleSubview = subview
|
||||
NSLayoutConstraint.activate(verticalConstraints)
|
||||
}
|
||||
|
||||
if lastSubviewBottomConstraint == nil || arrangedSubviews[visibleSubviews.last!] !== lastSubviewBottomConstraint?.0 {
|
||||
lastSubviewBottomConstraint?.1.isActive = false
|
||||
// this constraint needs to have low priority so that during the collapse/expand animation, the content container is the view that shrinks/expands
|
||||
let lastVisibleSubview = arrangedSubviews[visibleSubviews.last!]
|
||||
let constraint = lastVisibleSubview.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||
constraint.isActive = !isCollapsed
|
||||
constraint.priority = .defaultLow
|
||||
lastSubviewBottomConstraint = (lastVisibleSubview, constraint)
|
||||
}
|
||||
|
||||
NSLayoutConstraint.activate(verticalConstraints)
|
||||
|
||||
lastSubviewBottomConstraint?.isActive = false
|
||||
// this constraint needs to have low priority so that during the collapse/expand animation, the content container is the view that shrinks/expands
|
||||
lastSubviewBottomConstraint = subviews.last(where: { !$0.isHidden })!.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||
lastSubviewBottomConstraint!.isActive = !isCollapsed
|
||||
lastSubviewBottomConstraint!.priority = .defaultLow
|
||||
|
||||
zeroHeightConstraint.isActive = isCollapsed
|
||||
|
||||
|
@ -133,7 +138,7 @@ class StatusContentContainer<ContentView: ContentTextView, PollView: StatusConte
|
|||
// don't call setNeedsUpdateConstraints b/c that destroys/recreates a bunch of other constraints
|
||||
// if there is no lastSubviewBottomConstraint, then we already need a constraint update, so we don't need to do anything here
|
||||
if let lastSubviewBottomConstraint {
|
||||
lastSubviewBottomConstraint.isActive = !collapsed
|
||||
lastSubviewBottomConstraint.1.isActive = !collapsed
|
||||
zeroHeightConstraint.isActive = collapsed
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,11 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
|
|||
$0.layer.masksToBounds = true
|
||||
$0.layer.cornerCurve = .continuous
|
||||
$0.tintColor = .secondaryLabel
|
||||
let heightConstraint = $0.heightAnchor.constraint(equalToConstant: TimelineStatusCollectionViewCell.timelineReasonIconSize)
|
||||
heightConstraint.identifier = "TimelineReason-Height"
|
||||
NSLayoutConstraint.activate([
|
||||
// this needs to be lessThanOrEqualTo not just equalTo b/c otherwise intermediate layouts are broken
|
||||
$0.heightAnchor.constraint(lessThanOrEqualToConstant: TimelineStatusCollectionViewCell.timelineReasonIconSize),
|
||||
heightConstraint,
|
||||
$0.widthAnchor.constraint(equalTo: $0.heightAnchor),
|
||||
])
|
||||
}
|
||||
|
@ -60,16 +62,20 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
|
|||
$0.addSubview(contentVStack)
|
||||
metaIndicatorsView.translatesAutoresizingMaskIntoConstraints = false
|
||||
$0.addSubview(metaIndicatorsView)
|
||||
let avatarTopConstraint = avatarImageView.topAnchor.constraint(equalTo: $0.topAnchor)
|
||||
avatarTopConstraint.identifier = "Avatar-Top"
|
||||
let metaIndicatorsTopConstraint = metaIndicatorsView.topAnchor.constraint(equalTo: avatarImageView.bottomAnchor, constant: 4)
|
||||
metaIndicatorsTopConstraint.identifier = "MetaIndicators-Top"
|
||||
NSLayoutConstraint.activate([
|
||||
avatarImageView.leadingAnchor.constraint(equalTo: $0.leadingAnchor),
|
||||
avatarImageView.topAnchor.constraint(equalTo: $0.topAnchor),
|
||||
avatarTopConstraint,
|
||||
contentVStack.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: 8),
|
||||
contentVStack.trailingAnchor.constraint(equalTo: $0.trailingAnchor),
|
||||
contentVStack.topAnchor.constraint(equalTo: $0.topAnchor),
|
||||
contentVStack.bottomAnchor.constraint(equalTo: $0.bottomAnchor),
|
||||
metaIndicatorsView.leadingAnchor.constraint(greaterThanOrEqualTo: $0.leadingAnchor),
|
||||
metaIndicatorsView.trailingAnchor.constraint(equalTo: avatarImageView.trailingAnchor),
|
||||
metaIndicatorsView.topAnchor.constraint(equalTo: avatarImageView.bottomAnchor, constant: 4),
|
||||
metaIndicatorsTopConstraint,
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -177,6 +183,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
|
|||
$0.tintAdjustmentMode = .normal
|
||||
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
||||
$0.addTarget(self, action: #selector(collapseButtonPressed), for: .touchUpInside)
|
||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
}
|
||||
|
||||
let contentContainer = StatusContentContainer<StatusContentTextView, StatusPollView>(useTopSpacer: false).configure {
|
||||
|
@ -316,17 +323,12 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
|
|||
}
|
||||
|
||||
mainContainerTopToReblogLabelConstraint = mainContainer.topAnchor.constraint(equalTo: timelineReasonHStack.bottomAnchor, constant: 4)
|
||||
mainContainerTopToReblogLabelConstraint.identifier = "MainContainerTopToReblog"
|
||||
mainContainerTopToSelfConstraint = mainContainer.topAnchor.constraint(equalTo: statusContainer.topAnchor, constant: 8)
|
||||
// when flipping between topToReblog and topToSelf constraints, the framework sometimes thinks both of them should be active simultaneously
|
||||
// even though the code never does that; so let this one get broken temporarily
|
||||
mainContainerTopToSelfConstraint.priority = .init(999)
|
||||
mainContainerTopToSelfConstraint.identifier = "MainContainerTopToSelf"
|
||||
mainContainerBottomToActionsConstraint = mainContainer.bottomAnchor.constraint(equalTo: actionsContainer.topAnchor, constant: -4)
|
||||
mainContainerBottomToSelfConstraint = mainContainer.bottomAnchor.constraint(equalTo: statusContainer.bottomAnchor, constant: -6)
|
||||
|
||||
let metaIndicatorsBottomConstraint = metaIndicatorsView.bottomAnchor.constraint(lessThanOrEqualTo: statusContainer.bottomAnchor, constant: -6)
|
||||
// sometimes during intermediate layouts, there are conflicting constraints, so let this one get broken temporarily, to avoid a bunch of printing
|
||||
metaIndicatorsBottomConstraint.priority = .init(999)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
// why is this 4 but the mainContainerTopSelfConstraint constant 8? because this looks more balanced
|
||||
timelineReasonHStack.topAnchor.constraint(equalTo: statusContainer.topAnchor, constant: 4),
|
||||
|
@ -335,13 +337,14 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
|
|||
|
||||
mainContainer.leadingAnchor.constraint(equalTo: statusContainer.leadingAnchor, constant: 16),
|
||||
mainContainer.trailingAnchor.constraint(equalTo: statusContainer.trailingAnchor, constant: -16),
|
||||
mainContainerBottomToActionsConstraint,
|
||||
|
||||
actionsContainer.leadingAnchor.constraint(equalTo: statusContainer.leadingAnchor, constant: 16),
|
||||
actionsContainer.trailingAnchor.constraint(equalTo: statusContainer.trailingAnchor, constant: -16),
|
||||
// yes, this is deliberately 6. 4 looks to cramped, 8 looks uneven
|
||||
actionsContainer.bottomAnchor.constraint(equalTo: statusContainer.bottomAnchor, constant: -6),
|
||||
|
||||
metaIndicatorsBottomConstraint,
|
||||
metaIndicatorsView.bottomAnchor.constraint(lessThanOrEqualTo: statusContainer.bottomAnchor, constant: -6),
|
||||
])
|
||||
|
||||
updateActionsVisibility()
|
||||
|
@ -590,8 +593,14 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
|
|||
}
|
||||
|
||||
timelineReasonHStack.isHidden = hideTimelineReason
|
||||
mainContainerTopToReblogLabelConstraint.isActive = !hideTimelineReason
|
||||
mainContainerTopToSelfConstraint.isActive = hideTimelineReason
|
||||
// do this to make sure the currently active constraint is deactivated first
|
||||
if hideTimelineReason {
|
||||
mainContainerTopToReblogLabelConstraint.isActive = false
|
||||
mainContainerTopToSelfConstraint.isActive = true
|
||||
} else {
|
||||
mainContainerTopToSelfConstraint.isActive = false
|
||||
mainContainerTopToReblogLabelConstraint.isActive = true
|
||||
}
|
||||
|
||||
doUpdateUI(status: status, precomputedContent: precomputedContent)
|
||||
|
||||
|
@ -690,11 +699,11 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
|
|||
}
|
||||
|
||||
private func updateActionsVisibility() {
|
||||
if Preferences.shared.hideActionsInTimeline {
|
||||
if Preferences.shared.hideActionsInTimeline && !actionsContainer.isHidden {
|
||||
actionsContainer.isHidden = true
|
||||
mainContainerBottomToSelfConstraint.isActive = true
|
||||
mainContainerBottomToActionsConstraint.isActive = false
|
||||
} else {
|
||||
mainContainerBottomToSelfConstraint.isActive = true
|
||||
} else if !Preferences.shared.hideActionsInTimeline && actionsContainer.isHidden {
|
||||
actionsContainer.isHidden = false
|
||||
mainContainerBottomToSelfConstraint.isActive = false
|
||||
mainContainerBottomToActionsConstraint.isActive = true
|
||||
|
|
Loading…
Reference in New Issue