3

In my UIViewController I create CALayer with image1.

CALayer *imageLayer = [CALayer layer];
[imageLayer setBounds:CGRectMake(0.0, 0.0, 241.0, 430.0)];
[imageLayer setPosition:CGPointMake(160.0, 60.0)];
[imageLayer setAnchorPoint:CGPointMake(0.5, 0.0)];
[imageLayer setContents:(id)[UIImage imageNamed:@"image1.png".CGImage];
[imageLayer setContentsGravity:kCAGravityResizeAspect];
[self.view.layer addSublayer:imageLayer];

Later in UIViewController I want to change image by pushing it using CATransition.

CATransition *t = [CATransition animation];
t.beginTime = [self.view.layer convertTime:CACurrentMediaTime() + 6.5 fromLayer:nil];
t.type = kCATransitionPush;
t.subtype = kCATransitionFromRight;
[imageLayer setContents:(id)[UIImage imageNamed:@"image2.png"].CGImage];    
[imageLayer addAnimation:t forKey:nil];

When running the code I get image2 from the beginning, transition works though by pushing itself after 6.5 seconds. Where's the image1? What's wrong?

Correct answer: Thanks both Jacky Boy and Tiago Almeida. I corrected the code removing the line

t.beginTime = [self.view.layer convertTime:CACurrentMediaTime() + 6.5 fromLayer:nil];

and adding 'dispatch_after'.

Now it works:

CATransition *t = [CATransition animation];
t.type = kCATransitionPush;
t.subtype = kCATransitionFromRight;
[imageLayer setContents:(id)[UIImage imageNamed:@"image2.png"].CGImage];    
[imageLayer addAnimation:t forKey:nil];
[CATransaction begin];
NSTimeInterval delayInSeconds = 6.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [imageLayer setContents:(id)[UIImage imageNamed:@"image2.png"].CGImage];
    [imageLayer addAnimation:t forKey:nil];
});
[CATransaction commit];

Thanks again, guys!

Palindrome
  • 161
  • 2
  • 11

3 Answers3

3

Remove the following line:

t.beginTime = [self.view.layer convertTime:CACurrentMediaTime() + 1.5 fromLayer:nil];

This is what delaying the animation, so the layer is setting the new content, and only after the layer is animated. If you want to specify the duration of the animation:

[CATransaction begin];
[CATransaction setAnimationDuration:1.0];

CATransition *t = [CATransition animation];
t.type = kCATransitionPush;
t.subtype = kCATransitionFromRight;
[_layer setContents:(id)[UIImage imageNamed:@"image2.jpg"].CGImage];
[_layer addAnimation:t forKey:nil];


[CATransaction commit];

Or a smaller version:

CATransition *t = [CATransition animation];
t.type = kCATransitionPush;
[t setDuration:2.5];
t.subtype = kCATransitionFromRight;
[_layer setContents:(id)[UIImage imageNamed:@"image2.jpg"].CGImage];
[_layer addAnimation:t forKey:nil];

I would prefer this:

CATransition *transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.duration = .25f;
transition.subtype = kCATransitionFromRight;

_layer.actions = @{@"contents": transition};

And when you want to change the content:

[_layer setContents:(id)[UIImage imageNamed:@"image2.jpg"].CGImage];
Rui Peres
  • 25,741
  • 9
  • 87
  • 137
  • To achieve the 6.5 seconds delay he can always wrap with dispatch_after. This way the animation will be performed instantly :) – Tiago Almeida Jan 16 '14 at 15:20
  • Tiago Almeida, I appreciate your help, but could you provide code for setting a delay with dispatch_after? – Palindrome Jan 16 '14 at 15:59
  • just start typing dispatch_after in the XCode and press tab twice and you have a template prepared by XCode for that (code in comments is a mess :p) – Tiago Almeida Jan 16 '14 at 16:04
  • Jacky Boy, nice solution with [_layer setContents:(id)[UIImage imageNamed:@"image2.jpg"].CGImage]; but later I will change image back and I want it push back from the opposite side of the screen. – Palindrome Jan 16 '14 at 16:38
1

You are creating the imageLayer but configuring the currentCharacterImageLayer.

Mundi
  • 79,884
  • 17
  • 117
  • 140
0

The correct solution here is actually to use the fillMode property on the transition. I.e. (in Swift sorry)

transition.fillMode = .backwards

From the docs

If you delay the start of an animation, you might also want to set the fillMode property to kCAFillModeBackwards. This fill mode causes the layer to display the animation’s start value, even if the layer object in the layer tree contains a different value. Without this fill mode, you would see a jump to the final value before the animation starts executing. Other fill modes are available too.

dispatch_after will work for some situations, but will not work if you need to compose animations, e.g. using CATransaction

Thanks for posting the question, it put me on the right track to finding the answer

RyanM
  • 4,474
  • 4
  • 37
  • 44