Can't identify crash cause in UIView.animate(withDuration) 'animations' closure

169 views Asked by At

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:

Animated progress view as a gif

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:

stack trace 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?

1

There are 1 answers

2
DonMag On

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:

    UIView.animate(withDuration: 0.4, delay: 0, options: .transitionCrossDissolve) {
        self.labelProgress.text = "\(Int(percentage * 100))%"
    }

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(...):

    UIView.transition(with: self.pctLabel, duration: 0.4, options: .transitionCrossDissolve, animations: {
        self.pctLabel.text = "\(Int(percentage * 100))%"
    })

Second, you might be running into a problem with your availableValueContainerWidth calculation, 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 to 100%, we might get something like this:

enter image description here

Giving the label a fixed width (calculate the width needed for the max value text of "100%"), it can look like this:

enter image description here

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.