Commit A Different Animation(s) While Another Animation is ending - ios

I'm trying to create a 'wave' effect of UIView animations.
I have numerous layers to animate. I'm currently animating them with animateWithDuration:delay:options:animations:completion:
I have the views displayed in a stack so for example:
-------------------------------
| VIEW 1 |
-------------------------------
| VIEW 2 |
-------------------------------
| VIEW 3 |
-------------------------------
| VIEW 4 |
-------------------------------
| VIEW 5 |
-------------------------------
| VIEW 6 |
-------------------------------
This is important to know because UIView transitions are not what I'm going after.
I can easily animate them and concurrently activate the next view animation in the completion block of the previous view. So this is how it's currently set up:
[UIView animateWithDuration:0.4f animations:^{
self.view1.alpha = 1.0;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.4f animations:^{
self.view2.alpha = 1.0;
} completion:^(BOOL finished) {
etc...
}];
}];
What i would like to do, instead of concurrently activating the next view animation I would like it to activate while the previous views animation is ending. So say, the last half of time in the animation process, start the next views animation to get a wave effect. None of the UIViewAnimationOptions match my criteria, and I've also researched the developer forums for a similar goal, with no results.
What I have tried:
The workaround that I've tried is adding a delay to the next UIView animations inside the animations block as opposed to the completion block, but I feel this isn't a healthy take on the matter. Ex:
[UIView animateWithDuration:0.4f animations:^{
//Other animations...
self.view1.alpha = 1.0f
[UIView animateWithDuration:0.4f delay:0.2f options:UIViewAnimationOptionAllowUserInteraction animations:^{
self.view2.alpha = 1.0f;
} completion:^(BOOL finished) {
//repeat..
}];
}];
As you can see my work around, well, works. But as I stated above, it doesn't feel an iota of healthy. What am I missing?
EDIT
I inevitably went the route of using UIViewAnimationOptions by delaying and autoreversing the durations:
NSTimeInterval duration = 3.0;
NSTimeInterval delay = 0.5f;
[UIView animateWithDuration:duration delay:0.0f options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction) animations:^{
self.view1.alpha = 1.0f;
} completion:^(BOOL finished) {
}];
[UIView animateWithDuration:duration delay:delay options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction) animations:^{
self.view2.alpha = 1.0f;
} completion:^(BOOL finished) {
}];
[UIView animateWithDuration:duration delay:delay*2 options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction) animations:^{
self.view3.alpha = 1.0f;
} completion:^(BOOL finished) {
}];
etc...

You can use CALayer animation instead of UIView animation to achieve this. Just configure the begin time, duration and the animation on each view's layer. Here is a quick Swift example:
for (var i, v) in enumerate(views) {
let animation = CABasicAnimation(keyPath:"opacity")
animation.toValue = 0.0
animation.duration = 0.4
animation.beginTime = CACurrentMediaTime() + 0.2 * i
v.layer.addAnimation(animation, forKey: "wave")
}

Related

How to repeat shake animation indefinitely or X times using animateWithDuration?

The code below came from this SO question: UIView shake animation.
This shake animation is perfect, except it stops after one iteration. How do you repeat the animation indefinitely, or how do you cause it to repeat it X times?
There are other SO answers on repeating UIView animations that suggest using CAKeyframeAnimation or CABasicAnimation, but this question is different. The goal is to reproduce this exact animation, with the "springy" effect from usingSpringWithDamping and initialSpringVelocity.
Using Autoreverse and Repeat don't reproduce the desired effect because the the initial translation is outside the animation block.
view.transform = CGAffineTransformMakeTranslation(20, 0);
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
view.transform = CGAffineTransformIdentity;
} completion:nil];
If you need exact same code few times than put that code in method and do recursion, something like that:
- (void)animateCount:(NSInteger)count {
if (count == 0) {
return;
}
view.transform = CGAffineTransformMakeTranslation(20, 0);
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
view.transform = CGAffineTransformIdentity;
} completion:^{
count--;
[self animateCount:count];
}];
}
Use (UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat) in options to repeat it indefinitely
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:(UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut) animations:^{
someView.frame = someFrame1;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5f animations:^{
someView.frame = someFrame2;
} completion:nil];
}];

Strange behavior animating a UIView using [UIView animateKeyframesWithDuration]

I'm trying to create an effect where three arrows go from visible to invisible alternatively.
I made a simple algorithm where every arrow would start from a different alpha value (if there is 3 arrows, the first one would start at alpha=1, the second one at alpha=0.6667, the third one at alpha=0.3337). I then start a key frame animation that:
Change the arrow opacity from its current alpha to 0 (the duration is computed)
Set instantly the arrow opacity to 1
Change the arrow opacity from 1 to the first value set
However it seems that some step are skipped for some reason.
A simple example:
[UIView animateKeyframesWithDuration:2 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear | UIViewKeyframeAnimationOptionRepeat animations:^{
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0 animations:^{
_animatedView.alpha = 0.5;
}];
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.5 animations:^{
_animatedView.alpha = 0;
}];
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0 animations:^{
_animatedView.alpha = 1;
}];
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{
_animatedView.alpha = 0.5;
}];
} completion:nil];
In that case it should go to 0.5 instantly, 0.5 to 0 in 1 second, go to 1 instantly, 1 to 0.5 in 1 second. It should therefore do a seamless transition that looks like the view is appearing and disappearing but it looks like the animation gets stuck on alpha=0.5 for a few time.
Theoretically, the effect should be the same as if using this key frame animation:
[UIView animateKeyframesWithDuration:2 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear | UIViewKeyframeAnimationOptionRepeat animations:^{
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0 animations:^{
_animatedView.alpha = 1;
}];
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:1 animations:^{
_animatedView.alpha = 0;
}];
} completion:nil];
In case you want to animate N views in the same way:
CGFloat count = [self.animatedViews count];
CGFloat period = 1.0f / count;
__weak NSArray *weakViews = self.animatedViews;
[UIView animateKeyframesWithDuration:2.0f delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear | UIViewKeyframeAnimationOptionRepeat animations:^{
for (NSUInteger index = 0; index < count; ++index) {
UIView *animatedView = weakViews[index];
CGFloat startDelay = period * index;
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0 animations:^{
animatedView.alpha = startDelay;
}];
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:startDelay animations:^{
animatedView.alpha = 0.0f;
}];
[UIView addKeyframeWithRelativeStartTime:startDelay relativeDuration:0.0f animations:^{
animatedView.alpha = 1.0f;
}];
[UIView addKeyframeWithRelativeStartTime:startDelay relativeDuration:(1.0f - startDelay) animations:^{
animatedView.alpha = startDelay;
}];
}
} completion:nil];

Animating UIView alpha according to views location

I am animating a view using this code but would like to set the alpha of the orb according to its location . is this possible as I understand that the animation block is executed immediately and I have no access to its location
self.orbView.center = self.arrowView.center;
self.orbView.alpha = 1;
[UIView animateWithDuration:1.0 delay:0 options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat animations:^{
self.orbView.center = CGPointMake(arrowCornerRight,self.orbView.center.y);
self.orbView.alpha = 0;
} completion:^(BOOL finished) {
}];

When the uibutton wobbles it pushes uibuttons near itself

I added wobble function to my 2 buttons. The startWobble and stopWobble methods work properly:
- (void)startWobble {
_hint1.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-10));
_hint2.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-10));
[UIView animateWithDuration:0.001
delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse)
animations:^ {
_hint1.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(10));
_hint2.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(10));
}
completion:NULL
];
}
- (void)stopWobble {
[UIView animateWithDuration:0.25
delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear)
animations:^ {
_hint1.transform = CGAffineTransformIdentity;
_hint2.transform = CGAffineTransformIdentity;
}
completion:NULL
];
}
However, I tested my app on the simulator, and when the buttons start to wobble they push the other buttons near themself.The buttons then stop to wobble everything looks fine again. I can't figure the problem out. I've used these methods and never faced a problem like this before.
Any suggestions?

iOS - Stop a repeating UIAnimation?

I have an animation that needs to be repeated until I decided to stop it.
How can I stop in animation after a button click?
[UIView animateWithDuration:0.2 delay:0 options:(UIViewAnimationCurveLinear | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat) animations:^{
CGAffineTransform transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(5));
self.transform = transform;
} completion:^(BOOL finished){
}];
You have to add one other option UIViewAnimationOptionAllowUserInteraction ...
You should try this:
[UIView animateWithDuration:2 delay:0 options:UIViewAnimationCurveLinear | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction animations:^
{
view.frame = CGRectMake(0, 100, 200, 200);
} completion:^(BOOL finished)
{
if(! finished) return;
}];
And to stop the animation use this:
[view.layer removeAllAnimations];
U can make use of CABasicAnimation instead.
CABasicAnimation *appDeleteShakeAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
appDeleteShakeAnimation.autoreverses = YES;
appDeleteShakeAnimation.repeatDuration = HUGE_VALF;
appDeleteShakeAnimation.duration = 0.2;
appDeleteShakeAnimation.fromValue = [NSNumber numberWithFloat:-degreeToRadian(5)];
appDeleteShakeAnimation.toValue=[NSNumber numberWithFloat:degreeToRadian(5)];
[self.layer addAnimation:appDeleteShakeAnimation forKey:#"appDeleteShakeAnimation"];
Then when u want to stop it you can just call
[self.layer removeAnimationForKey:#"appDeleteShakeAnimation"];

Resources