skip to Main Content

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
})

2

Answers


  1. 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
        })
    })
    
    Login or Signup to reply.
  2. 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
    
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search