Flipping the UILabel - ios

I want to use the CABasicAnimation to flip the UILabel. Animation will repeat forever and will change the text of UILabel between two different values.
- (void)animateLabel
{
[self.myLabel.layer addAdnimation:[self labelAnimation] forKey:#"flip"];
}
- (CAAnimation*)labelAnimation
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform"];
[animation setRepeatCount:NSIntegerMax];
[animation setAutoreverses:YES];
[animation setDuration:2.0];
[animation setDelegate:self];
CATransform3D transform = CATransform3DMakeRotation(M_PI_2, 1, 0, 0);
[animation setToValue:[NSValue valueWithCATransform3D:transform]];
return animation;
}
Now, I tried using the delegate but the delegate method is only when the animation first begins. Rather I need to be able to know the the label completes one cycle. Is there some convenience method or way to do it with CALayer or do I have to use CADisplay link or timer ? I would like to thank you before hand for helping me.

To get notified on each cycle, setRepeatsCount to 1. You'll then get notified in the delegate animationDidStop:finished. You would then simply add the same animation again.
- (void)animateLabel
{
[self.myLabel.layer addAdnimation:[self labelAnimation] forKey:#"flip"];
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
[self animateLabel];
// do other stuff
}
- (CAAnimation*)labelAnimation
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform"];
[animation setRepeatCount:1];
[animation setAutoreverses:YES];
[animation setDuration:2.0];
[animation setDelegate:self];
CATransform3D transform = CATransform3DMakeRotation(M_PI_2, 1, 0, 0);
[animation setToValue:[NSValue valueWithCATransform3D:transform]];
return animation;
}

No, this is the way you're supposed to do it according to the documentation.
Setting this property to HUGE_VALF will cause the animation to repeat forever.

Related

Identifying CAAnimation in animationDidStop callback failing

How to identify CAAnimation after completion? I tried KVO by setting a string value as animationID and checking it in animationDidStop method. But KVO returns an integer sometimes instead of a string animationID and the app is crashing.
Here is my code:
-(void)moveLayer:(CALayer*)layer to:(CGPoint)point duration:(NSTimeInterval)duration animationID:(NSString*)animationid
{
// Prepare the animation from the current position to the new position
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.fromValue = [layer valueForKey:#"position"];
[animation setDuration:duration];
animation.toValue = [NSValue valueWithCGPoint:point];
animation.delegate=self;
[animation setValue:animationid forKey:#"animationID"];
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
// Update the layer's position so that the layer doesn't snap back when the animation completes.
layer.position = point;
// Add the animation, overriding the implicit animation.
[layer addAnimation:animation forKey:#"position"];
}
and callback is:
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
{
if (flag) {
id animationIDString=[animation valueForKey:#"animationID"];
// this is coming as an int value sometimes and the code below this fails ..
if([animationIDString isEqual:#"animation1"]) {
//animation is animation1
_movieBalloonImageView.hidden=NO;
_storiesBalloonImageView.hidden=NO;
_rhymesBalloonImageView.hidden=NO;
[self.navigationController popViewControllerAnimated:NO];
}
}
}
What can be the possible reason? Is it because the animation gets destroyed before this delegate gets called?
I am setting removedOnCompletion to NO and fillmode to kCAFillModeForwards.But the code is not working as expected.
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
Or is there any alternative method to do this? I am calling the moveLayer method in several places in the viewController.

Animate UIView using Core Animation?

I am having a little trouble getting a UiView to animate properly using core animation. Here is my code:
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setFromValue:[NSValue valueWithCGPoint:self.view.layer.position]];
[animation setToValue:[NSValue valueWithCGPoint:CGPointMake(250, self.view.layer.position.y)]]; //I want to move my UIView 250 pts to the right
[animation setDuration:1];
[animation setDelegate:self]; //where self is the view controller of the view I want to animate
[animation setRemovedOnCompletion:NO];
[self.view.layer addAnimation:animation forKey:#"toggleMenu"]; //where self.view returns the view I want to animate
I have also implemented the following delegate method:
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
if(flag)
[self.view.layer setTransform:CATransform3DMakeTranslation(250, 0, 0)]; //set layer to its final position
}
I am trying to make it so that the UIView moves 250 points to the right. However, when the animation is triggered, my view starts to move but the animations seems to end before the view moves 250 points to the right, resulting in the UIView 'teleporting' to its final position. I can't seem to figure out what is causing this behavior.
I have also tried using the UIView method +(void)animateWithDuration:animations: and this approach works perfectly. However, I am trying to achieve a subtle 'bounce' effect and I'd much rather achieve it using the setTimingFunction:functionWithControlPoints: method rather than having multiple callbacks.
Any help and suggestions are appreciated.
Try like this
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
rotationAnimation.fromValue=[NSValue valueWithCGPoint:CGPointMake([view center].x, [view center].y)];
rotationAnimation.toValue=[NSValue valueWithCGPoint:CGPointMake([view center].x+250, [view center].y)];
rotationAnimation.duration = 1.0+i;
rotationAnimation.speed = 3.0;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[view.layer addAnimation:rotationAnimation forKey:#"position"];
If you are aiming to move your view, your code should go like this:
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setFromValue:[NSValue valueWithCGPoint:self.view.layer.position]];
CGPoint p = self.view.layer.position;
p.x += 250;
self.view.layer.position = p;
[animation setDuration:1];
[animation setRemovedOnCompletion:NO];
[self.view.layer addAnimation:animation forKey:#"toggleMenu"];
You should really change your view's(layer's) position. Otherwise, your view will stay still at its previous position when your animation removed. That's a difference between UIView Animation and Core Animation.
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setFromValue:[NSValue valueWithCGPoint:view.layer.position]];
CGPoint newPosition = CGPointMake(200, 300);
view.layer.position = p;
[animation setDuration:0.5f];
[animation setRemovedOnCompletion:NO];
[group setFillMode:kCAFillModeForwards];
[[view layer] addAnimation:animation forKey:#"position"];

how to build in and build out a layer?

I want to build in a layer and build out it after 5 seconds. but I don't know how to assign the final value to the layer. my code is as following:
-(CABasicAnimation *) buildIn{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"opacity"];
animation.duration = 2.0f;
[animation setFromValue:[NSNumber numberWithFloat:0.0f]];
[animation setToValue:[NSNumber numberWithFloat:1.0f]];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
return animation;
}
-(CABasicAnimation *) buildOut{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"opacity"];
animation.duration = 2.0f;
animation.beginTime = CACurrentMediaTime() + 5;
[animation setFromValue:[NSNumber numberWithFloat:1.0f]];
[animation setToValue:[NSNumber numberWithFloat:0.0f]];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
return animation;
}
- (IBAction)startAnimation:(id)sender {
[self.textView.layer addAnimation:[self buildIn] forKey:#"transparent"];
self.textView.layer.opacity = 1.0f;//I need to set the opacity to 1 after buildIn animation
[self.textView.layer addAnimation:[self buildOut] forKey:#"transparent"];
self.textView.layer.opacity = 0.0f;//ERROR: this is the final layer value, but I need to assign it after the build out animation, not here.
}
I think I can use a completion block to do it, but I can't find it.
If you set a delegate on your buildIn animation, then when it is finished the -animationDidStop:finished: message will be sent to the delegate. Then you can start your buildOut animation.
CAAnimation delegate reference

Fade-transition a rect in a UIView

So I want to redraw a rectangle in a view and then transition to it with a fade. This is what I'm using now:
[self setNeedsDisplayInRect:changedRect];
CATransition *animation = [CATransition animation];
[animation setDuration:0.25];
[animation setType:kCATransitionFade];
[[self layer] addAnimation:animation forKey:nil];
This however fades the entire layer. How can I go about fading just changedRect?
Thanks,
Alec

iOS UIView properties don't animate with CABasicAnimation

I can imagine a far amount of sighs when people see this question popup again. However, I have read through a lot of information both here, in the documentation and via Google and still haven't found a solution. So here goes nothing.
I'm trying to recreate the Facebook login screen, where the spacing and position animates with the keyboard to allow the user to still see all the input fields and login button.
This works when I use the kCAFillModeForwards and set removedOnCompletion to NO. But, as said in another thread here on SO, this seems to only visually change properties and the actual tap position isn't changed. So when the user is seemingly tapping an input field, iOS interprets it as a tap on the background.
So I tried setting the new position and size but when I do that the animation doesn't play, it just snaps to the new position. Putting it before the addAnimation call and after it, with and without transactions, it doesn't make any difference.
The delegate methods are still called, but you can't visually see any animation.
if([notification name] == UIKeyboardWillShowNotification) {
CGRect currBounds = self.loginTable.tableHeaderView.layer.bounds;
CGSize newSize = CGSizeMake(self.loginTable.tableHeaderView.bounds.size.width, 60);
CGPoint newPos = CGPointMake(self.loginTable.layer.position.x, self.loginTable.layer.position.x - 50);
//[CATransaction begin];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"bounds.size"];
[animation setToValue:[NSValue valueWithCGSize:newSize]];
[animation setDelegate:self];
[self.loginTable.tableHeaderView.layer addAnimation:animation forKey:#"headerShrinkAnimation"];
CABasicAnimation *formPosAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
[formPosAnimation setToValue:[NSValue valueWithCGPoint:newPos]];
[formPosAnimation setDelegate:self];
//formPosAnimation.removedOnCompletion = NO;
//formPosAnimation.fillMode = kCAFillModeForwards;
[self.loginTable.layer addAnimation:formPosAnimation forKey:#"tableMoveUpAnimation"];
//[CATransaction commit];
[self.loginTable.tableHeaderView.layer setBounds:CGRectMake(currBounds.origin.x, currBounds.origin.y, newSize.width, newSize.height)];
[self.loginTable.layer setPosition:newPos];
}
I have found a way to make it work, can't say if it's the best way to do it but it seems to be working now.
The key thing was to combine almost everything. So I had to keep removedOnCompletion and fillModeon my animations while also updating the position in my animationDidStop method. It works without setting the two animation parameters as well, but you can see a small flicker in the end.
- (void)keyboardWillChange:(NSNotification *)notification {
newSize = CGSizeZero;
newPos = CGPointZero;
if([notification name] == UIKeyboardWillShowNotification) {
newSize = CGSizeMake(self.loginTable.tableHeaderView.bounds.size.width, 60);
newPos = CGPointMake(self.loginTable.layer.position.x, self.loginTable.layer.position.x - 50);
} else {
newSize = CGSizeMake(self.loginTable.tableHeaderView.bounds.size.width, 150);
newPos = CGPointMake(self.loginTable.layer.position.x, self.loginTable.layer.position.x + 50);
}
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"bounds.size"];
[animation setToValue:[NSValue valueWithCGSize:newSize]];
[animation setDelegate:self];
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[self.loginTable.tableHeaderView.layer addAnimation:animation forKey:#"headerShrinkAnimation"];
/*-----------------------------*/
CABasicAnimation *formPosAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
[formPosAnimation setToValue:[NSValue valueWithCGPoint:newPos]];
[formPosAnimation setDelegate:self];
formPosAnimation.removedOnCompletion = NO;
formPosAnimation.fillMode = kCAFillModeForwards;
[self.loginTable.layer addAnimation:formPosAnimation forKey:#"tableMoveUpAnimation"];
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
NSLog(#"Animation did stop");
CGRect currBounds = self.loginTable.tableHeaderView.layer.bounds;
[self.loginTable.tableHeaderView.layer setBounds:CGRectMake(currBounds.origin.x, currBounds.origin.y, newSize.width, newSize.height)];
[self.loginTable.layer setPosition:newPos];
[self.loginTable.tableHeaderView.layer removeAnimationForKey:#"headerShrinkAnimation"];
[self.loginTable.layer removeAnimationForKey:#"tableMoveUpAnimation"];
}

Resources