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...
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.
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;
}