Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

App crashing because of paused animation and NSInternalInconsistencyException

I animate a UIView on and off the screen by pulling up and swiping down. I use this code to do that:

private var _animator: AnyObject?
@available(iOS 10.0, *)
var animator: UIViewPropertyAnimator? {
    get {
        return _animator as? UIViewPropertyAnimator
    }
    set {
        _animator = newValue
    }
}
var haveTheySwiped = false
var haveTheyNotSwiped = true

@available(iOS 10.0, *)
func handlePan(recognizer: UIPanGestureRecognizer) {
    let vel = recognizer.velocity(in: self)
        switch recognizer.state {
        case .began:
            if vel.y < 0 && !haveTheySwiped {
                animator = UIViewPropertyAnimator(duration: 1, curve: .easeOut, animations: {
                    self.backgroundImage.frame = self.backgroundImage.frame.offsetBy(dx: 0, dy: -603)
                })
                haveTheySwiped = true
                isScanVisible = true
            } else if vel.y > 0 && haveTheySwiped {
                animator = UIViewPropertyAnimator(duration: 1, curve: .easeOut, animations: {
                    self.backgroundImage.frame = self.backgroundImage.frame.offsetBy(dx: 0, dy: 603)
                })
                haveTheySwiped = false
                isScanVisible = false
            }
            animator?.pauseAnimation()
            print(backgroundImage.frame.origin)
        case .changed:
            let translation = recognizer.translation(in: backgroundImage)
            if vel.y < 0 {
                animator?.fractionComplete = translation.y / -603
            } else if vel.y > 0 && haveTheySwiped == true {
                animator?.fractionComplete = translation.y / 603
            }
        case .ended:
            animator?.continueAnimation(withTimingParameters: nil, durationFactor: 0)
        case .possible: break
        default: break
        }
}

But, recently I have come across a bug. When I try to pull up, the UIView does not appear and then when I lift my finger up, the app crashes with this error:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'It is an error to release a paused or stopped property animator. Property animators must either finish animating or be explicitly stopped and finished before they can be released.'

Has anyone come across this? Would love some help figuring out what is causing this to happen. Any help would be immensely appreciated. Thanks so much in advance!

Cheers, Thoe

like image 973
Theo Strauss Avatar asked Jul 22 '17 16:07

Theo Strauss


4 Answers

In case somebody still gets this crash in 2019 or later, I had a similar issue with an animator I only needed to run for 25% of its fraction complete (to create a subtle blur on a collectionView cell).

The crash would occur when the cell would be deinit or when the VC would be popped from the navController.

Inside the UI setup of my cell, I had the below implementation

Before: leading to crash

 animator?.fractionComplete = 0.25

After: no more crash

 animator?.fractionComplete = 0.25
 animator?.stopAnimation(true) 
 animator?.finishAnimation(at: .current)

This did the trick for me.

like image 113
Edouard Barbier Avatar answered Nov 14 '22 21:11

Edouard Barbier


The error here is generally due to the fact that the System is trying to clear the animator before it is back to .inactive state.

I don see all your codes.

But generally, something like this will crash

  private func animating(){
    let animation = UIViewPropertyAnimator(duration: 3, curve: .linear) {
        [weak self] in
        self?.view.backgroundColor = .red
    }

    animation.startAnimation()
    animation.pauseAnimation()
}

When the animating function is completed, the system will try to clear the UIViewPropertyAnimator you have created (the property does not live outside of the define scope), but the animation is paused, which means it is still at the .active state (the same goes to animation.stopAnimation(), just that in this case it is .stop state), and like I mention above, anytime when you try to clear the animator before it is back on .inactive state, the system will throw an error.

So, the solution in this case, if you wish to pause or stop animation halfway, you need to find a way to retain the animator.

For example, you can create a property in the viewController like

var animation : UIViewPropertyAnimator!

But you need to make sure that the animator goes back to .inactive state before you allow the system to clear it, or else the crash will just happen again.

like image 45
progammingBeignner Avatar answered Nov 14 '22 21:11

progammingBeignner


Implementing stopAnimation(Bool) then finishAnimation(at: UIViewAnimatingPosition) should clear up the problem.

This class adopts the UIViewAnimating and UIViewImplicitlyAnimating protocols, which define the methods for starting, stopping, and modifying your animations. For more information about the methods of those protocols, see UIViewAnimating and UIViewImplicitlyAnimating .

https://developer.apple.com/documentation/uikit/uiviewanimating

func stopAnimation(Bool) Stops the animations at their current positions. Required.

func finishAnimation(at: UIViewAnimatingPosition) Finishes the animations and returns the animator to the inactive state. Required.

like image 38
solenoid Avatar answered Nov 14 '22 22:11

solenoid


Swift 5.x:

animator?.stopAnimation(true)
if let animator = animator, animator.state != .inactive {
    animator.finishAnimation(at: .current)
}

Fixed problem in our case.

like image 21
atereshkov Avatar answered Nov 14 '22 21:11

atereshkov