Reset animation state during user interaction - ios

I have a circle that users can touch, when you touch it it changes size then reverts back to its original size to indicate you touched it. I want users to be able to touch it as often as they like regardless of whether its currently animating or not. I updated my animation to use the UIViewAnimationOptionAllowUserInteraction flag which does allow me to touch it during the animation but it doesnt behave as I'd expect.
I think I need to somehow stop the currently playing animation, reset the size to normal and then play it again, is this correct and if so how do I do it? If not what do I do?
- (void)pop{
[view.layer removeAllAnimations];
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction // reverse back to original value
animations:^{
// scale down 20%
self.transform = CGAffineTransformMakeScale(0.8, 0.8);
} completion:^(BOOL finished) {
self.transform = CGAffineTransformMakeScale(1, 1);
}];
}

self.btn = [[UIButton alloc] initWithFrame:CGRectMake(200, 200, 100, 30)];
[self.btn addTarget:self action:#selector(tapDown:) forControlEvents:UIControlEventTouchDown];
[self.btn addTarget:self action:#selector(tapUp:) forControlEvents:UIControlEventTouchUpInside];
- (IBAction)tapDown:(id)sender
{
[UIView animateWithDuration:0.2
delay:0.0
options: UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction
animations:^{
// scale down 20%
self.btn.transform = CGAffineTransformMakeScale(0.8, 0.8);
} completion:nil];
}
- (IBAction)tapUp:(id)sender {
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction // reverse back to original value
animations:^{
// scale up to 100%
self.btn.transform = CGAffineTransformMakeScale(1, 1);
} completion:nil];
}

Sure this is not what you want? When the player taps and pop is called the animation should start over?
- (void)pop{
self.transform = CGAffineTransformMakeScale(1, 1); // If you want the animation to start over you need to reset the size before staring it again
[view.layer removeAllAnimations];
[UIView animateWithDuration:0.2
delay:0.0
options: options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionCurveEaseOut| UIViewAnimationOptionAllowUserInteraction // reverse back to original value
animations:^{
// scale down 20%
self.transform = CGAffineTransformMakeScale(0.8, 0.8);
} completion:^(BOOL finished) {
self.transform = CGAffineTransformMakeScale(1, 1);
}];
}

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

iOS continuous fade-out and fade-in animation

In objective-c, I want to add fade in and fade out animation to a UIView. I want to do the fade-out and fade-in animation continuously. To illustrate it, what effect I want to have is as following:
fade out - fade in - fade out - fade in - ...
What I'm trying is as following:
-(void)fadeOutIn:(UIView *)view duration:(NSTimeInterval)duration
{
static CGFloat alpha = 1.0;
[UIView animateWithDuration:duration
delay:0.0
options: UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveEaseInOut
animations:^{
alpha = abs((int)(alpha - 1.0));
view.alpha = alpha;
}
completion:^(BOOL finished){
}];
}
I also tried following:
-(void)fadeOutIn:(UIView *)view duration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration
delay:0.0
options: UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveEaseInOut
animations:^{
view.alpha = 0.0;
}
completion:^(BOOL finished){
[UIView animateWithDuration:duration
delay:0.0
options: UIViewAnimationOptionRepeat|UIViewAnimationOptionCurveEaseInOut
animations:^{
view.alpha = 1.0;
}
completion:^(BOOL finished){
}];
}];
}
But the issue is they worked all the same, i.e., the UIView will fade out properly and SUDDENLY show up(not fade in), then fade out again...
Seems the fade in animation not work at all. Does anybody know what's wrong of my code please? Any suggestion will be highly appreciated.
Try this
[UIView animateKeyframesWithDuration:1 delay:0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse|UIViewAnimationCurveEaseInOut animations:^{
self.customIV.alpha = 0;
} completion:^(BOOL finished) {
}];
Here's this, Vigor, this does work, but I had to modify the code, this is somewhat dangerous code, due to it's constant recursiion, but you'll get the point:
-(void)fadeOutIn
{
[UIView animateWithDuration:1
delay:0.0
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
[[self contentView] emailField].alpha = 0.0;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1
delay:0.0
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
[[self contentView] emailField].alpha = 1.0;
}
completion:^(BOOL finished){
[self fadeOutIn];
}];
}];
}
I would really rethink how this works, I used it on a button in my view and it will recurse constanly, until the view is removed from the superview. I had to hardcode the button in the code.
[UIView animateWithDuration:duration
delay:0.0
options: UIViewAnimationOptionRepeat| UIViewAnimationOptionAutoreverse|UIViewAnimationOptionCurveEaseInOut
animations:^{
view.alpha = 0.0;
}
completion:nil];
Just call this once
Try this....
YourView.alpha = 1;// set the initial value of alpha
[UIView animateWithDuration:1 delay:0
options: UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
animations:^{
YourView.alpha = 0; // set the max value of alpha
} completion:nil];
This solution is for Swift 3,4 and 5
UIView.animateKeyframes(withDuration: 1, delay: 0, options: [UIView.KeyframeAnimationOptions(rawValue: UIView.AnimationOptions.repeat.rawValue), UIView.KeyframeAnimationOptions(rawValue: UIView.KeyframeAnimationOptions.RawValue(UIView.AnimationCurve.easeInOut.rawValue)), UIView.KeyframeAnimationOptions(rawValue: UIView.AnimationOptions.autoreverse.rawValue)] , animations: {
self.logoCenterImageView.alpha = 0
}) { finished in
}

How to animate an UILabel from small to its original size?

Actually i am using RESlider in my app. In the menu table view there is a profile image and aside to it there is a notification label. Now i want is that when the user presses the hamburger menu the notification label(orange label with 999 number) should animate from a tiny dot to its original size. How to achieve this??
Put this in viewDidAppear
-(void)viewDidAppear:(BOOL)animated{
self.label.transform = CGAffineTransformMakeScale(0.01, 0.01);
[UIView animateWithDuration:0.5 animations:^{
self.label.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
}];
}
myTextLabel.transform = CGAffineTransformMakeScale(0.3, 0.3);
[UIView animateWithDuration:2.0
delay: 0.1
options: UIViewAnimationOptionBeginFromCurrentState
animations:^{
myTextLabel.transform = CGAffineTransformMakeScale(1.5, 1.5); //grow
}
completion:^(BOOL finished){
myTextLabel.transform = CGAffineTransformMakeScale(1, 1);
}];
Change the transform scale of your label, like this :
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut
animations:^{
timerLabel.transform = CGAffineTransformScale(timerLabel.transform, 0.7, 0.7);
}
completion:nil];

Animate UIButton from current state

I want to create button which has "elastic" press down animation.
Animation description:
On press:
scale it on 80% in 0.1s
scale it on 90% in 0.1s // when pressed it should stay at 90% scale
On release:
scale it on 110% in 0.1s
scale it on 100% in 0.1s // when released - scale should finish at 100%
I tried to implement this with code:
-(void) pressedAnimation {
[UIView animateWithDuration:kAnimationTime
delay:0.0
options: UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.transform = CGAffineTransformMakeScale(.8f, .8f);
}completion:^(BOOL finished){
if (finished) {
[UIView animateWithDuration:kAnimationTime
delay:0.0
options: UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.transform = CGAffineTransformMakeScale(.9f, .9f);
}completion:^(BOOL finished){
}];
}
}];
}
-(void) releasedAnimation {
[UIView animateWithDuration:kAnimationTime
delay:0.0
options: UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.transform = CGAffineTransformMakeScale(1.1f, 1.1f);
}completion:^(BOOL finished){
if (finished) {
[self.layer removeAllAnimations];
[UIView animateWithDuration:kAnimationTime
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
}completion:^(BOOL finished){
}];
}
}];
}
Animation works as expected only if "releasedAnimation" is called when "pressedAnimation" is already completed. Else I got very strange effects.
I tried to use [self.layer removeAllAnimations] in both methods but it doesn't work as expected. It seems like UIViewAnimationOptionBeginFromCurrentState doesn't work. What am I doing wrong? Is there a better way to create such an animation?
You could abstract this behavior out to a utility class. Try something like this:
For the .h:
+(void)buttonBobble:(UIButton *)button actionWhenDone:(void(^)(BOOL))action;
For .m:
+(void)buttonBobble:(UIButton *)button actionWhenDone:(void (^)(BOOL))action
{
button.transform = CGAffineTransformMakeScale(0.8f, 0.8f);
[UIView animateWithDuration:0.5f animations:^{
button.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
}completion:action];
}
Of course, you may have to tweak this for the right look you want. You'll also want to add the UIViewAnimationOptionBeginFromCurrentState if you want it to pick up right where the button is at.

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

Resources