2

I'm trying to change the speed of an Explicit iOS Animation (so think CABasicAnimation or CAKeyFrameAnimation) using CAAnimation.CurrentMediaTime. This method is much better than stoping the animation, then restarting it with a new duration. I'm SO CLOSE to making this all work but I'm having one little problem. I can Start, Pause, Resume, and change speeds sometimes, but the change speed function is buggy. Can anyone shed some light onto the situation? I have some very simple code and an example project below.

Class level variables

UIView MyImage;
double timeSincePause = 0;
double pauseTime = 0;

My Pause Function (That works)

MyImage.Layer.Speed = 0.0f;
MyImage.Layer.TimeOffset = MyImage.Layer.ConvertTimeFromLayer(CAAnimation.CurrentMediaTime(), MyImage.Layer) - timeSincePause;

My Stop Function (That works)

pauseTime = MyImage.Layer.TimeOffset;
MyImage.Layer.Speed = 1.0f;
MyImage.Layer.TimeOffset = 0.0f;
MyImage.Layer.BeginTime = 0.0f;
timeSincePause = (MyImage.Layer.ConvertTimeFromLayer(CAAnimation.CurrentMediaTime(), MyImage.Layer) - pauseTime);
MyImage.Layer.BeginTime = timeSincePause;

My Change Speed Function (That doesn't work) To reproduce the problem simply launch the project and hit the change speed button a few times. Notice that when it goes from 100% to 30% speed it changes speed correctly, but when it goes from 30% speed to 100% speed the position of the object jumps ahead as if the speed was never changed to 30%. This is clearly a problem with the Time value I'm saving, I've experimented and couldn't get it to work.

MyImage.Layer.TimeOffset = MyImage.Layer.ConvertTimeFromLayer(CAAnimation.CurrentMediaTime(), MyImage.Layer);
MyImage.Layer.BeginTime = CAAnimation.CurrentMediaTime();
// I have it set so the speed changes back and forth between 100% and 30%.
if ( MyImage.Layer.Speed == 1.0f ) 
    MyImage.Layer.Speed = 0.3f;
else
    MyImage.Layer.Speed = 1.0f;

Here's a link to the very simple example project writtin in C# with Xamarin Studio iOS: https://www.dropbox.com/s/ndfo12rbemsnp1s/ChangeAnimationSpeed.zip

--------- Additional Information ----------

I've determined the problem is that I'm not factoring in the Time lost/gained as a result of changing speed... I'm just not sure how to take that into account yet... So I basically need to add another class level variable called "totalTimeLostResultingFromSpeedChange" and keep track of that and factor that in somehow...

LampShade
  • 2,675
  • 5
  • 30
  • 60

1 Answers1

3

I finally solved this problem after working on it for days on and off! Here's the C# Xamarin iOS project. And below are the key functions. This is some valuable stuff for anyone not using a game engine to animate in iOS.

Video

Class level variables

    UIView MyImage;

    double currentPauseTime = 0;
    double totalPauseTime = 0;
    double currentChangeSpeedTime = 0;
    double currentTimeLostResultingFromSpeedChange = 0;
    double totalTimeLostResultingFromSpeedChange = 0;

    float currentSpeed = 0;

Key Functions

    public void PauseAnimation()
    {
        if ( MyImage.Layer.Speed != 0.0f )
        {
            Update_TotalTimeLostResultingFromSpeedChange();

            MyImage.Layer.Speed = 0.0f;
            MyImage.Layer.TimeOffset = MyImage.Layer.ConvertTimeFromLayer(CAAnimation.CurrentMediaTime(), MyImage.Layer) - totalPauseTime - totalTimeLostResultingFromSpeedChange;
            currentPauseTime = MyImage.Layer.TimeOffset;
        }
    }


    public void ResumeAnimation()
    {
        if ( MyImage.Layer.Speed == 0.0f )
        {
            MyImage.Layer.Speed = 1.0f;
            MyImage.Layer.TimeOffset = 0.0f;
            MyImage.Layer.BeginTime = 0.0f;

            // Because we reset the BeginTime and TimeOffset, Reset the totalTimeLostResultingFromSpeedChange
            Reset_TotalTimeLostResultingFromSpeedChange();

            totalPauseTime = (MyImage.Layer.ConvertTimeFromLayer(CAAnimation.CurrentMediaTime(), MyImage.Layer) - currentPauseTime);
            MyImage.Layer.BeginTime = totalPauseTime;
        }


        ChangeAnimationSpeed( currentSpeed );
    }


    public void ChangeAnimationSpeed( float speed )
    {
        if ( speed != 0.0f )
        {
            Update_TotalTimeLostResultingFromSpeedChange();

            currentChangeSpeedTime = MyImage.Layer.ConvertTimeFromLayer(CAAnimation.CurrentMediaTime(), MyImage.Layer);
            MyImage.Layer.TimeOffset = currentChangeSpeedTime - totalPauseTime - totalTimeLostResultingFromSpeedChange;
            MyImage.Layer.BeginTime = CAAnimation.CurrentMediaTime();

            currentSpeed = speed;
            MyImage.Layer.Speed = currentSpeed;
        }
    }


    public void Update_TotalTimeLostResultingFromSpeedChange()
    {
        double value = MyImage.Layer.ConvertTimeFromLayer(CAAnimation.CurrentMediaTime(), MyImage.Layer) - currentChangeSpeedTime;
        currentTimeLostResultingFromSpeedChange = value - (value * MyImage.Layer.Speed);
        totalTimeLostResultingFromSpeedChange += currentTimeLostResultingFromSpeedChange;
    }


    public void Reset_TotalTimeLostResultingFromSpeedChange()
    {
        totalTimeLostResultingFromSpeedChange = 0;
    }
LampShade
  • 2,675
  • 5
  • 30
  • 60
  • It works! thank you very much. i've been trying to fix it for days. your code safe me a lot of time. – thanyaj Dec 01 '18 at 13:10