UIViewControllerAnimatedTransitioning in iOS7 - viewWillAppear not called - ios

I'm trying to get the hang of the new viewController animations in ios7 but I am encountering a road bump when trying to use core animation.
Im finding that after the transition has completed, the controller of the 'to view' does not seem to be receiving the typical view lifecycle methods, i.e. viewWillAppear and viewDidAppear. I'm calling completeTransition: but that doest seem to be enough -- is this typical behavior? Or am I doing something wrong? here is my code:
-(void)executeDismissalAnimation:(id<UIViewControllerContextTransitioning>)transitionContext{
UIView *inView = [transitionContext containerView];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
toViewController.view.hidden = NO;
[CATransaction begin];
[CATransaction setCompletionBlock:^{
[fromViewController.view removeFromSuperview];
[transitionContext completeTransition:YES];
}];
CGFloat duration = 0.4f;
CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.values = [self floatValuesFromValue:1.f toValue:1.5f withEasingFunction:ExponentialEaseInOut];
scaleAnimation.duration = duration;
scaleAnimation.fillMode = kCAFillModeForwards;
scaleAnimation.removedOnCompletion = YES;
[fromViewController.view.layer addAnimation:scaleAnimation forKey:nil];
CAKeyframeAnimation *alphaAnimation = [CAKeyframeAnimation animationWithKeyPath:#"opacity"];
alphaAnimation.values = [self floatValuesFromValue:1.f toValue:0.f withEasingFunction:LinearInterpolation];
alphaAnimation.duration = duration*2;
alphaAnimation.fillMode = kCAFillModeForwards;
alphaAnimation.removedOnCompletion = YES;
[fromViewController.view.layer addAnimation:alphaAnimation forKey:nil];
CAKeyframeAnimation *scaleAnimationB = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimationB.values = [self floatValuesFromValue:0.6f toValue:1.0f withEasingFunction:ExponentialEaseInOut];
scaleAnimationB.duration = duration;
scaleAnimationB.beginTime = CACurrentMediaTime()+0.3;
scaleAnimationB.fillMode = kCAFillModeForwards;
scaleAnimationB.removedOnCompletion = NO;
[toViewController.view.layer addAnimation:scaleAnimationB forKey:nil];
CAKeyframeAnimation *alphaAnimationB = [CAKeyframeAnimation animationWithKeyPath:#"opacity"];
alphaAnimationB.values = [self floatValuesFromValue:0.5f toValue:1.f withEasingFunction:LinearInterpolation];
alphaAnimationB.duration = duration;
alphaAnimationB.beginTime = CACurrentMediaTime()+0.3;
alphaAnimationB.fillMode = kCAFillModeForwards;
alphaAnimationB.removedOnCompletion = NO;
[toViewController.view.layer addAnimation:alphaAnimationB forKey:nil];
[CATransaction commit];
}

Related

Flickering in simple animation chain

I'm trying to animate a pulsting circle. But after the downward scale, there's something of a flickering effect. Any ideas to why that is? Here's my code so far:
- (instancetype)init
{
self = [super init];
if (self) {
if (!self.path) {
self.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(0, 0) radius:7 startAngle:0.0*(M_PI/180.0) endAngle:360.0*(M_PI/180.0) clockwise:YES].CGPath;
}
[self animateCircleUpward];
}
return self;
}
-(void)animateCircleUpward {
[CATransaction begin];
[CATransaction setCompletionBlock:^{[self animateCircleDownward];}];
CABasicAnimation * scaleUpwardAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.xy"];
scaleUpwardAnimation.fromValue = #(0.7);
scaleUpwardAnimation.toValue = #(1.0);
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.fromValue = #(1.0);
opacityAnimation.toValue = #(0.6);
self.fillColor = [UIColor greenColor].CGColor;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
NSArray *animations = #[scaleUpwardAnimation, opacityAnimation];
animationGroup.duration = 0.4;
animationGroup.animations = animations;
[self addAnimation:animationGroup forKey:#"pulse"];
[CATransaction commit];
}
-(void)animateCircleDownward {
[CATransaction begin];
[CATransaction setCompletionBlock:^{[self animateCircleUpward];}];
CABasicAnimation * scaleDownwardAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.xy"];
scaleDownwardAnimation.fromValue = #(1.0);
scaleDownwardAnimation.toValue = #(0.7);
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.fromValue = #(0.6);
opacityAnimation.toValue = #(1.0);
self.fillColor = [UIColor greenColor].CGColor;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
NSArray *animations = #[scaleDownwardAnimation, opacityAnimation];
animationGroup.duration = 0.4;
animationGroup.animations = animations;
[self addAnimation:animationGroup forKey:#"pulse"];
[CATransaction commit];
}
#end
Thanx in advance!
In regards to your actual question you likely need to set your CAAnimationGroup's fillMode property to kCAFillModeForward. The default value is kCAFillModeRemoved, so you are likely glimpsing the non-presentation layer between calls back and forth from your Down and Up methods.
That said, it appears that what you're trying to create is a cyclic animation through back and forth calls between your Up and Down methods via CATransaction's completionBlock. This seems an incredibly inefficient approach; why not try something more like:
-(void)scaleAndOpacityAnimations
{
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.xy”];
scaleAnimation.fromValue = #(0.7f);
scaleAnimation.toValue = #(1.0f);
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#“opacity”];
opacityAnimation.fromValue = #(1.0f);
opacityAnimation.toValue = #(0.6f);
CAAnimationGroup *scaleAndOpacity = [CAAnimationGroup animation];
scaleAndOpacity.animations = #[scaleAnimation, opacityAnimation];
scaleAndOpacity.autoReverses = YES;
scaleAndOpacity.repeatCount = HUGE_VALF;
//OP had no value indicated for the duration in posted code, I’m including it here for completion
scaleAndOpacity.duration = 0.5f;
[self addAnimation:scaleAndOpacity forKey:#“pulse”];
}
Which will simply repeat infinitely many times without having to instantiate new CAAnimations on each cycle.

Mimic UIAlertController show animation

So I created my own custom UIView that looks like an alert, and now I want to add show and hide animations.
I want to mimic Apples default animations for the AlertController. The dismissal is an simple fade animation, however I'm not sure how to articulate the show animation, it's almost like a fade in mixed with a shrink.
Anyways if anybody know how to recreate it I'd really appreciate it.
This is my final result:
This gif is doing it no justice but it's very similar to UIAlertControllers animations.
-(void)alerterShowAnimation {
self.alpha = 0;
self.hidden = NO;
self.transform = CGAffineTransformMakeScale(1.2, 1.2);
[UIView animateWithDuration:0.3 animations:^{
self.alpha = 1.0f;
self.containerView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
}];
[UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.transform = CGAffineTransformIdentity;
} completion:nil];
}
Instead of UIView animation, try using the core animation.
I have created a simple UIView with grey background, and added the animation below:
- (void)addAlertPopUpAnimationWithBeginTime:(CFTimeInterval)beginTime andFillMode:(NSString *)fillMode andRemoveOnCompletion:(BOOL)removedOnCompletion completion:(void (^)(BOOL finished))completionBlock
{
CAMediaTimingFunction *linearTiming = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
if (completionBlock)
{
CABasicAnimation *representativeAnimation = [CABasicAnimation animationWithKeyPath:#"not.a.real.key"];
representativeAnimation.duration = 0.200;
representativeAnimation.delegate = self;
[self.layer addAnimation:representativeAnimation forKey:#"AlertPopUp"];
}
CAKeyframeAnimation *proPicOpacityAnimation = [CAKeyframeAnimation animationWithKeyPath:#"opacity"];
proPicOpacityAnimation.duration = 0.200;
proPicOpacityAnimation.values = #[#(0.000), #(0.525), #(1.000)];
proPicOpacityAnimation.keyTimes = #[#(0.000), #(0.500), #(1.000)];
proPicOpacityAnimation.timingFunctions = #[linearTiming, linearTiming];
proPicOpacityAnimation.beginTime = beginTime;
proPicOpacityAnimation.fillMode = fillMode;
proPicOpacityAnimation.removedOnCompletion = removedOnCompletion;
[[self layer] addAnimation:proPicOpacityAnimation forKey:#"alertPopUp_Opacity"];
CAKeyframeAnimation *proPicScaleXAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale.x"];
proPicScaleXAnimation.duration = 0.200;
proPicScaleXAnimation.values = #[#(0.304), #(0.345), #(0.300)];
proPicScaleXAnimation.keyTimes = #[#(0.000), #(0.500), #(1.000)];
proPicScaleXAnimation.timingFunctions = #[linearTiming, linearTiming];
proPicScaleXAnimation.beginTime = beginTime;
proPicScaleXAnimation.fillMode = fillMode;
proPicScaleXAnimation.removedOnCompletion = removedOnCompletion;
[[self layer] addAnimation:proPicScaleXAnimation forKey:#"alertPopUp_ScaleX"];
CAKeyframeAnimation *proPicScaleYAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale.y"];
proPicScaleYAnimation.duration = 0.200;
proPicScaleYAnimation.values = #[#(0.381), #(0.435), #(0.389)];
proPicScaleYAnimation.keyTimes = #[#(0.000), #(0.500), #(1.000)];
proPicScaleYAnimation.timingFunctions = #[linearTiming, linearTiming];
proPicScaleYAnimation.beginTime = beginTime;
proPicScaleYAnimation.fillMode = fillMode;
proPicScaleYAnimation.removedOnCompletion = removedOnCompletion;
[[self layer] addAnimation:proPicScaleYAnimation forKey:#"alertPopUp_ScaleY"];
CAKeyframeAnimation *proPicTranslationXAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.translation.x"];
proPicTranslationXAnimation.duration = 0.200;
proPicTranslationXAnimation.values = #[#(0.000), #(0.194), #(0.683)];
proPicTranslationXAnimation.keyTimes = #[#(0.000), #(0.500), #(1.000)];
proPicTranslationXAnimation.timingFunctions = #[linearTiming, linearTiming];
proPicTranslationXAnimation.beginTime = beginTime;
proPicTranslationXAnimation.fillMode = fillMode;
proPicTranslationXAnimation.removedOnCompletion = removedOnCompletion;
[[self layer] addAnimation:proPicTranslationXAnimation forKey:#"alertPopUp_TranslationX"];
CAKeyframeAnimation *proPicTranslationYAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.translation.y"];
proPicTranslationYAnimation.duration = 0.200;
proPicTranslationYAnimation.values = #[#(0.000), #(11.242), #(0.581)];
proPicTranslationYAnimation.keyTimes = #[#(0.000), #(0.500), #(1.000)];
proPicTranslationYAnimation.timingFunctions = #[linearTiming, linearTiming];
proPicTranslationYAnimation.beginTime = beginTime;
proPicTranslationYAnimation.fillMode = fillMode;
proPicTranslationYAnimation.removedOnCompletion = removedOnCompletion;
[[self layer] addAnimation:proPicTranslationYAnimation forKey:#"alertPopUp_TranslationY"];
}
- (void)addAlertPopUpAnimationWithCompletion:(void (^)(BOOL finished))completionBlock
{
[self addAlertPopUpAnimationWithBeginTime:0 andFillMode:kCAFillModeBoth andRemoveOnCompletion:NO completion:completionBlock];
}
When you want to add animations, call right after you add your view as subview:
You need to subclass the UIView and put the code in there.
Later you can call:
[_yourView addAlertPopUpAnimationWithCompletion:^(BOOL finished) {
//do your stuff after animation is finished
}];
The animation looks like this:

Add a bounce effect to a flip animation

I animated a flip for a pie chart I have. Here is my code"
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:0.5];
scaleAnimation.duration = 1.0f;
scaleAnimation.removedOnCompletion = NO;
[self.pieChart addAnimation:scaleAnimation forKey:#"scale"];
animationDidStop:finished:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
animation.fromValue = [NSNumber numberWithFloat:0.5];
animation.toValue = [NSNumber numberWithFloat:1.0];
animation.duration = 1.0f;
[self.pieChart addAnimation:animation forKey:#"scale"];
How can I add a bounce effect to the second animation? (The animation in animationDidStop:finished:)
Go with a CAKeyFrameAnimation instead of two CABasicAnimation:
CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale.x"];
scaleAnimation.keyTimes = #[#(0), #(0.5), #(0.9), #(1)];
scaleAnimation.values = #[#(1.0), #(0.5), #(1.1), #(1.0)];
scaleAnimation.duration = 1.0f;
scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
With keyTimes and values, you can specify multiple stances in your animation. Here, I made it start with a scale of 1, animating to a scale of 0.5 (your first animation values), then 1.1 (an overshoot to create a "bounce") and finally 1 (your second animation values). Tweak those to your liking!
If you want to do this animation in two parts, you can keep your first animation, then start a CAKeyFrameAnimation in the animationDidStop:finished: similar to this instead:
CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale.x"];
scaleAnimation.keyTimes = #[#(0), #(0.8), #(1)];
scaleAnimation.values = #[#(0.5), #(1.1), #(1.0)];
scaleAnimation.duration = 1.0f;
scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
If you instead want to use UIView animation blocks, you'll need to do something similar to this:
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.squareView.transform = CGAffineTransformMakeScale(0.5, 1);
} completion:^(BOOL finished) {
[UIView animateWithDuration:1 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
self.squareView.transform = CGAffineTransformIdentity;
} completion:nil];
}];
You will probably want to play with the params to tweak the animation.

iOS animation with duration and autoreverse

I am animating an UIImageView with the UIView block API. My animation fades the UIImageView In and Out continuously. How can I animate this only for a specific time duration ?
I have put up this piece of code
float tempDuration = 5.0;
[UIView animateWithDuration:tempDuration
delay:0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction
animations:^{
_imageView.alpha = 1;
}
completion:nil];
First of all allow me to introduce an article that is awesome to understand some features of the animations:
Controlling Animation Timming
In this article you have a part that shows what you want.
You want something like this:
Therefore you can configure that in the following way:
-(void) animateImageView:(UIImageView*) imageView
withDuration:(int) duration
withRepeatCount: (int) repeatCount {
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnim.fromValue = #1.0;
opacityAnim.toValue = #0.0;
opacityAnim.autoreverses = YES;
opacityAnim.duration = duration/2.0/repeatCount;
opacityAnim.removedOnCompletion = YES;
opacityAnim.repeatCount = repeatCount;
[imageView.layer addAnimation:opacityAnim forKey:nil];
}
This way you guarantee that your animation will always be 5 seconds and you can tune in the number of repetitions.
Maybe you could try CABasicAnimation instead if [UIView animateWithDiration: ... ]
it should be something like this:
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.0];
opacityAnim.autoreverses = YES;
opacityAnim.duration = duration;
opacityAnim.removedOnCompletion = YES;
opacityAnim.repeatCount = 5;
[imageView.layer addAnimation:opacityAnim forKey:nil];
You can do the following changes to do your work.
- (void)animateImageView{
static int animcount = 1;
float tempDuration = 1.0;
[UIView animateWithDuration:tempDuration
delay:0
options: UIViewAnimationOptionAutoreverse
animations:^{
self.imgView.alpha = 1;
}
completion:^(BOOL finished) {
if(finished && animcount<5){
animcount+=1;
[self animateImageView];
}
else if (finished && animcount==5){
[self.imgView.layer removeAllAnimations];
}
}];
}

ios CoreAnimation

I'm trying to skew an object cards it's an UIView I'm doing :
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:DEPLACEMENT_ANIMATION_DUREE_SECONDES_DROITE];
scene.carte17.layer.position = positionInit;
if (scene.carte17.angleCarte == 15.f) {
scene.carte17.transform = CGAffineTransformRotate(scene.carte17.transform, degreesToRadians(30));
scene.carte17.angleCarte = 45.f;
}
[UIView commitAnimations];
And it's working ! My card incline to 30° and it's good. I'm playing with setAnimationDuration to control the speed.
I would like to use CoreAnimation and kCAMediaTimingFunctionEaseInEaseOut to have a better animation but I'm not ok to do the same think with core animation I'm trying this :
[CATransaction begin];
[CATransaction setAnimationDuration:.5f];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
scene.carte17.layer.position = positionInit;
if (scene.carte17.angleCarte == 15.f) {
CABasicAnimation *animation;
animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0];
animation.toValue = [NSValue valueWithCGAffineTransform:CGAffineTransformRotate(scene.carte17.transform, degreesToRadians(30))];
animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];
animation.delegate = self;
[scene.carte17.layer addAnimation:animation forKey:#"rotationAnimation"];
scene.carte17.angleCarte = 45.f;
}
[CATransaction commit];
But it's not working ... Could you help me please !
Why not just call this:
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
But that is an overkill since UIViewAnimationCurveEaseInOut is the default value.

Resources