Home > Software design >  UIView.animate with limited repeat and autoreverse glitches on a real device
UIView.animate with limited repeat and autoreverse glitches on a real device

Time:10-05

I'm trying to make a simple scale animation of a button 1.0 -> 1.4, repeat 3 times and then stop, but I'm experiencing a glitch on a real device.

The code below works smoothly on a simulator, but on a real device (iPhone 12 Pro with iOS 16) it glitches and I can see the jump to scale 1.4 after animation is done, and then it jumps to scale 1.0 on completion where I set transform = .identity.

I've suspected UIView.modifyAnimations, but the same problem happens with the now deprecated UIView.setAnimationRepeatCount(3).

Here is the video of the glitch on the real device: https://i.imgur.com/x1LJ9Ox.mp4

UIView.animate(withDuration: 0.5, delay: 0, animations: {
    UIView.modifyAnimations(withRepeatCount: 3, autoreverses: true) {
        self.titleButton.transform = CGAffineTransformMakeScale(1.4, 1.4)
    // self.titleButton.layoutIfNeeded()  // doesn't change anything
    }
}, completion: { _ in
    self.titleButton.transform = .identity
})

CodePudding user response:

I've tested on my iphone 13 iOS 16, it glitches like you said above.

Maybe I'm wrong or misunderstand something but looks like something is changing in modifyAnimations(withRepeatCount:autoreverses:animations:).

In simulator, after the animation in modifyAnimations is done, it immediately go to completion. If in completion do nothing with the view, it will turn back to CGAffineTransformMakeScale(1.4, 1.4). But if the view has transform anything, it will turn to that transform.

But in real device, after the animation in modifyAnimations is done, the view will immediately turn back to the final of animation which here is CGAffineTransformMakeScale(1.4, 1.4) ( the simulator doesn't have this step) then after that go to completion. This cause the glitches like you said.

I have a workaround for this situation

UIView.animate(withDuration: 0.5, animations: {
    // stop at the half of the final repeat means no autoreverses accur at the end
    UIView.modifyAnimations(withRepeatCount: 2.5, autoreverses: true) {
        self.secondView.transform = CGAffineTransformMakeScale(1.4, 1.4)
     }
}, completion: { _ in
    // make final scale back here
    UIView.animate(withDuration: 0.5, animations: {
        self.secondView.transform = .identity
    })
})

CodePudding user response:

It looks like iOS 16 is updating the transform 1 event loop after the animation resulting in the glitch. One way to work around this is to leave the scale at 1.0 at the end of the animation itself by using a keyframe animation (which I found at this answer) and adding the code to repeat 3 times:

   UIView.animateKeyframes(withDuration: 1, delay: 0, options: [], animations: {
        UIView.modifyAnimations(withRepeatCount: 3, autoreverses: false) {
            
            UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
                self.titleButton.transform = CGAffineTransformMakeScale(1.4, 1.4)
            }
            UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
                self.titleButton.transform = .identity
            }
        }
    }) { (isFinished) in

    }
  • Related