5
votes

I am trying to create an interactive animation which uses a speed of 0.0 and manipulates the timeOffset property in response to user actions. I want the initial state of the animation to be somewhere in the middle of the defined animation as the user action allows the animation to proceed both forward and backward from the starting point.

Here is some sample code that is a simplified case of what I am trying to do.

@interface TestViewController : UIViewController {}
@property (strong, nonatomic) IBOutlet UIView * transformerView;
@property (strong, nonatomic) IBOutlet UISlider * slider;
@end

@implementation TCTestViewController {}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.slider.minimumValue = 0.0f;
    self.slider.maximumValue = 1.0f;
    self.slider.value = 0.5f;

    CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"transform"];
    animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(-320.0f, 0.0f, 0.0f)];
    animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(320.0f, 0.0f, 0.0f)];
    animation.duration = 1.0f;

    self.transformerView.layer.speed = 0.0;
    [self.transformerView.layer addAnimation:animation forKey:@"test"];

    //  *** THIS IS THE PROBLEM LINE ***
    self.transformerView.layer.timeOffset = 0.5;
}

- (IBAction)onSliderValueChanged {
    self.transformerView.layer.timeOffset = self.slider.value;
}

@end

This view controller has a view I am adding an animation that will translate in the X-axis from -320pt to +320pt, and I want to start it in the middle so there is no translation until the user interacts with it. I also have a slider that, when changed, sets the timeOffset property to match the slider position.

If I omit the problem line where timeOffset is set to 0.5, all works as expected, but the view starts out translated by -320pt. It then properly tracks the slider from that point on.

When the problem line is there, it all goes horribly wrong. The view still starts out translated by -320pt, and only tracks the slider when the slider value is above 0.5. Even then, the translation is only going from -320pt to 0pt, and never translates higher. For slider values below 0.5, there is no translation at all.

It makes no difference if I place the problem line before or after adding the animation to the layer. What does help is if I use -performSelector:withObject:afterDelay with a delay of 0.0 to set the initial timeOffset on a future iteration of the run loop, but before the user interacts with the slider. Unfortunately, this workaround is not practical for the real project as I need to create the animation on the fly as the user starts the interaction.

Why does the timeOffset property not work when set in the same run loop iteration as the animation was added to the layer? I think that what is happening is that when the timeOffset is set at this time, it becomes a new base time for the animation.

Can anyone suggest a way that I can create an animation that can be made interactive with the timeOffset property and set it to start at somewhere other than the beginning?

1
@matt - Good thought, but in the original project this sample was adapted from, the problem occurred in another method that was called well after viewDidLoad.GBegen

1 Answers

1
votes

I would try the following:

dispatch_async(dispatch_get_main_queue(), ^{
    self.transformerView.layer.timeOffset = 0.5;
});