Animating UIView alpha according to views location - ios

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) {
}];

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];
}];

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

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")
}

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];

When creating an animation to cause a UIImageView to go up and down (almost float) infinitely, how do I stop a pause at the end of the animation?

Here's the code I'm using to take a UIImageView and make it float up and down.
[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionRepeat animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.25 animations:^{
self.slider.transform = CGAffineTransformMakeTranslation(0, -5.0);
}];
[UIView addKeyframeWithRelativeStartTime:0.25 relativeDuration:0.5 animations:^{
self.slider.transform = CGAffineTransformMakeTranslation(0, 5.0);
}];
[UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{
self.slider.transform = CGAffineTransformMakeTranslation(0, 0.0);
}];
} completion:^(BOOL finished) {
}];
However, it comes out looking like this with this delay after the animation ends and before it restarts.
How do I make it fluid?
I'm not sure what effect you're going for exactly, but I think this gives you something like your code does without the delay. I usually do this by animating a constraint, rather than using transforms. In this example, I've made an IBOutlet (topCon) to the constraint to the top of the view:
-(IBAction)floatView:(id)sender {
static int i= 1;
static float duration = .25;
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
self.topCon.constant = self.topCon.constant - (5 * i);
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
i = (i == 1 || i == 2)? -2 : 2;
duration = 0.5;
[self floatView:self];
}];
}

Fade in and fade out a UIView while it is moving

It's easy enough to animate the view:
[UIView animateWithDuration:1.0
animations:^{theView.center = newCenter; theView.alpha = 0;}
completion:^(BOOL finished){
[theView removeFromSuperview];
}];
The problem is that when I add it as a subview, I want it to fade in and already look like it is moving. Right now it appears immediately, then moves and fades out.
So, I need to set it's initial alpha to zero, fade it quickly while it is moving, then fade it out. Is this possible with UIView animations? I can't have two competing animation blocks working on the same object right?
All you need to do is apply 2 animations back-to-back. Something like this ::
theView.alpha = 0;
[UIView animateWithDuration:1.0
animations:^{
theView.center = midCenter;
theView.alpha = 1;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1.0
animations:^{
theView.center = endCenter;
theView.alpha = 0;
}
completion:^(BOOL finished){
[theView removeFromSuperview];
}];
}];
So in the 1st second it will appear while moving and then in the next second it will fade out
Hope this helps
Put the initial alpha=0 outside animation block.
theView.alpha = 0;
[UIView animateWithDuration:1.0
animations:^{
theView.center = newCenter;
theView.alpha = 1;
}
completion:^(BOOL finished){
// Do other things
}];

Resources