Animation slowdown when building to M1 Mac

I have a simple app that lets a user navigate a maze. A token is moved around the maze in response to input by accelerometer, digital joystick, taps, or keyboard entry, depending upon settings and device capabilities. The view to the maze may be zoomed, in which case the maze pans to center the token. The main animations are to move the token, and to recenter the maze. An additional animation occurs if the token encounters certain prizes along the way. When that happens, the prize award is displayed over the token and then animated toward the center of the board.

Randomly and rarely, the animation changes to be very slow, when running a build to the Mac. Unlike the iOS simulators, I know of no way to toggle the animation speed when building to the Mac. Is there a way to toggle this? Am I somehow toggling it accidentally? When I do a test run on the Mac, I'm almost always using keyboard input, which is the arrow keys.

Unlike the iOS simulators, where halting and restarting after the animation has been slowed results in continued slow animations, if I halt the Mac build after the animation slows down and start again, it animates at normal speed.

Replies

  • Are the animations running at a lower speed, or are they running at a lower frame rate?
  • If you take an Instruments sample, is your app’s process busy or idle during the animation?
  • How are you setting up and starting these animations?

I have no idea about the frame rate. It is the speed that is lower - WAAY lower. Just like when you set the slow animations item on the iOS simulator. I'd not thought of running instruments. I'll try that when I have some time.

There is more than one way the animations are kicked off. The main one, the one that recenters the maze is done with a property of type UIViewPropertyAnimator. I call it mainAnimator. The basic logic goes like this:

if mainAnimator.isRunning {
    mainAnimator.stopAnimation(true)
    // some logic to set the bounds of my maze view.
    self.mainAnimator = UIViewPropertyAnimator(duration: newDuration, curve: .easeOut, animations: {
        self.fieldView.bounds.origin = CGPoint(x: relativeToPuzzleCenter.x, y: relativeToPuzzleCenter.y)
    })
} else {
    // some logic to calculate the duration of the animation
    self.mainAnimator = UIViewPropertyAnimator(duration: duration, curve: .easeInOut, animations: {
        self.fieldView.bounds.origin = CGPoint(x: relativeToPuzzleCenter.x, y: relativeToPuzzleCenter.y)
    }
}
mainAnimator.addCompletion ...

The completion block has a UIView.animate(withDuration) call in it. However, this is called only if the maze is completed. So this is not really relevant, since the issue happens before the maze is finished.

For the popup scores that animate off, the logic goes like this: Instantiate a view with appropriate label and add it as a subview. Use a timer to let it stay in place for some period of time before animating it off:

timer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) {
    // logic to do the popup animation
}

Popup animation:

sup.addSubview(labelHolder)
let animator = UIViewPropertyAnimator(duration: 2.0, curve: .linear) {
    labelHolder.center = endingPositionInSup
    labelHolder.alpha = 0
}
animator.addCompletion { (position) in
    label.removeFromSuperview()
}
animator.startAnimation()

For moving the token on the board, I use

UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut) {
    token.center = CGPoint(x: token.center.x + movement.x, y: token.center.y + movement.y)
} completion: { (finished) in
        // some stuff unrelated to animation.
}

Having said all this, I can play multiple games with hundreds of movements per game without the slowdown happening. When the slowdown happens, it's sudden. Possibly a clue: when the slowdown happens, the popups originate from the wrong place. I'll add an appropriate assert there, although, I don't see how a location error would cause unrelated animations to slow down.

Note that when the animation slowdowns happen it is ALL animations, including system animations that I didn't code. I should have mentioned that earlier.

P.S. I've been meaning to eliminate all instances of UIView.animate.... with property animators. But that's still a to-do.

You need not delve into this further. I've found a clue that will surely lead to the problem. I added a print for starting and ending positions of the popups, and I got this sample output.

Will animate popup mystery score from (168.5, 534.5) to (404.81994284151176, 487.23601143169765)
Will animate popup mystery score from (473.5, 473.5) to (nan, nan)

Obviously, I have a bug in calculating the destination. Thanks for looking. Interesting that this would slow animations.

With that clue, I solved the problem. I animate the popups from the token location to the center of the board. The problem was happening when the token location is at the center of the board. That caused a divide by zero when calculating the direction to the center. So in that case, I just go some other direction.

Unrelated, but an interesting observation I made today, trying to find out what was causing hesitation in the Mac version. It turns out the hesitation is caused by label.removeFromSuperview() shown above. Removing that line eliminated the hesitation. I moved the cleanup of those views to idle time or when the maze is completed. This is interesting to me because adding the view doesn't cause the hesitation. only removing it does.