I have a custom view that acts as a progress view, which is basically a simple set of views where I animate the leading constraints of the container that holds the value and of the view that acts as a 'filled' view, so it looks like this:
This is a reusable view and is used in several places in the app. Some of the users suffer a crash that is related to the animation in the update method of this custom view, and I can't reproduce it nor I can find the issue. The method looks like this:
extension GradientProgressView {
/// Animates the progress view to the desired % value and changes the value label
/// - Parameter percentage: the desired % value in range [0.0 - 1.0]
func updateProgress(percentage: Double) {
UIView.animate(withDuration: 0.4, delay: 0, options: .transitionCrossDissolve) {
self.labelProgress.text = "\(Int(percentage * 100))%"
}
// limit to 100% for correct constraint calculation
let percentageAdapted = CGFloat(min(percentage, 1.0))
// the available width for the value container to move left/right
let availableValueContainerWidth = backgroundView.frame.width - labelGradientView.frame.width
labelContainerLeadingConstraint.constant = min(availableValueContainerWidth * percentageAdapted, backgroundView.frame.width - 50)
foregroundLeadingConstraint.constant = labelContainerLeadingConstraint.constant
UIView.animate(withDuration: 0.6, delay: 0, options: .curveEaseOut) {
self.layoutIfNeeded()
}
}
}
More precisely, the crash happens in the animation block of the first UIView.animate call, which corresponds to the line 93 in the stacktrace (see below):
UIView.animate(withDuration: 0.4, delay: 0, options: .transitionCrossDissolve) {
self.labelProgress.text = "\(Int(percentage * 100))%"
}
Here's the stacktrace of the crash:
I've tried using the self as a weak reference in both animation blocks, but the crash reappeared. Could someone point out to me what am I doing wrong here?


It's very tough to debug a crash that can't be reproduced, but a couple comments (that may or may not have anything to do with the crash)...
First, this block:
doesn't really do anything, other than change the text.
If we want to cross-dissolve the text change in a label, we need to use
UIView.transition(...):Second, you might be running into a problem with your
availableValueContainerWidthcalculation, as you are allowing the label width to change based on its text. If the current value is only 1-digit, and we change it to100%, we might get something like this:Giving the label a fixed width (calculate the width needed for the max value text of "100%"), it can look like this:
Of course, we could use a variable-width label and "get around" the issue by using the center instead of leading ... but the cross-dissolve and width-change animation may also look a little quirky.
Again, really tough to debug a non-reproducible crash, but those two things may be something to look at.