I'm trying to make an animation where the text gets moved to the bottom as it fades out, at the same time it reappears at in the same location as before and does the same animation. here my code:
for (int i=0; i<10; i++)
{
[UIView animateWithDuration:0.5
delay:0.2f
options:UIViewAnimationCurveEaseInOut
animations:^{
productTextLabel.center = CGPointMake(381, 410);
productTextLabel.alpha = 0.0;
productTextLabel.center = CGPointMake(381, 340);
productTextLabel.alpha = 1;
}
completion:^(BOOL fin) {
}];
}
my problem is I am trying to make this animation happen more than one time. I am using a for loop but it only does it once.
You can use the options UIViewAnimationOptionRepeat and UIViewAnimationOptionAutoReverse in the animationWithDuration: method to repeat and reverse the animation automatically.
The whole method will become:
[UIView animateWithDuration:0.5
delay:0.2f
options:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoReverse
animations:^{
productTextLabel.center = CGPointMake(381, 410);
productTextLabel.alpha = 0.0;
}
completion:^(BOOL fin) {
}];
To cancel the animation you can call
[productTextLabel.layer removeAllAnimations];
Note: You should import QuartzCore to be able to call the removeAllAnimations function.
try this way, it worked for me:
__block void (^fadePart1)() = ^{ productTextLabel.center = CGPointMake(381.f, 410.f); productTextLabel.alpha = 0.f; };
__block void (^fadePart2)() = ^{ productTextLabel.center = CGPointMake(381.f, 340.f); productTextLabel.alpha = 1.f; };
__block void (^fadePart1Finished)(BOOL finished) = ^(BOOL finished) {
if (_willFinishAnimation == false) {
[UIView animateWithDuration:0.5f delay:0.2f options:UIViewAnimationCurveEaseInOut|UIViewAnimationOptionAllowUserInteraction animations:fadePart2 completion:^(BOOL finished) {
if (_willFinishAnimation == false) [UIView animateWithDuration:0.5f delay:0.2f options:UIViewAnimationCurveEaseInOut|UIViewAnimationOptionAllowUserInteraction animations:fadePart1 completion:fadePart1Finished];
}];
}
};
fadePart1Finished(false);
I had a global Boolean _willFinishAnimation; variable, before I set the animation it was false and when I want to finish the effect I set it true, that is it.
Related
I have a UITextField that is set to hidden, when I click the search button I want to make it visible but I want to create an animation like a slide-in, is it possible to recreate this effect if i'm using autoLayout?
The code I used for the animation is the current (it creates a fade-in effect):
if(!currentSrcState)
{
_searchField.hidden = NO;
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionTransitionCurlDown
animations:^ {
_searchField.alpha = 1.0;
}
completion:nil];
currentSrcState = YES;
}
else
{
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionTransitionCurlDown
animations:^ {
_searchField.alpha = 0;
}
completion:^(BOOL finished){
_searchField.hidden = YES;
}];
currentSrcState = NO;
}
What I want to acheive is that when the _searchField is hidden the constraint gets recalculated in order to move what is under the searchField up
Instead of set the button to hidden, set its alpha value to 0.0 before load the view. Then change your code to:
if(!currentSrcState)
{
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionTransitionCurlDown
animations:^ {
_searchField.alpha = 1.0;
}
completion:nil];
currentSrcState = YES;
}
else
{
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionTransitionCurlDown
animations:^ {
_searchField.alpha = 0.0;
}
completion:nil];
currentSrcState = NO;
}
In my Xcode project I have put a label on an xib.
I want to fade the label in and out continuously until the user taps the screen. When this happens I want a new view to appear.
Can anyone suggest how to do the fade-in/out?
Instead of nesting blocks and manually restarting your animations, you can use the option pair UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat to tell Core Animation you want the label to fade in and out continuously.
- (void)startAnimatingLabel
{
self.label.alpha = 0;
[UIView animateWithDuration:1
delay:0
options: UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
animations:^{
self.label.alpha = 1;
} completion:nil];
}
To stop the animations from running, just remove them from the label's layer.
- (IBAction)tap:(UITapGestureRecognizer *)sender
{
[self.label.layer removeAllAnimations];
self.label.alpha = 0;
[self presentNewView];
}
EDIT: A less abrupt way to finish would be to animate from the current view state to the final one (this will interrupt the current, repeating animation).
- (IBAction)tap:(UITapGestureRecognizer *)sender
{
[UIView animateWithDuration:1
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.label.alpha = 0;
} completion:^(BOOL finished){
[self presentNewView];
}];
}
You could put a pair of chained animation in a loop or call a function that holds the chained animation everytime until you encounter a user tap.
By Chained animation, I mean something like this (You can set the animation duration to suit your needs):
myLabel.alpha = 0.0;
[UIView animateWithDuration:1.0
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
myLabel.alpha = 1.0;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1.0
delay:1.0
options: UIViewAnimationCurveEaseOut
animations:^{
myLabel.alpha = 0.0;
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}];
The above code will first fade in your label and then fade it out. You can put that in a function and call it until you encounter user tap.
Create a CABasicAnimation and add it to your label:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"opacity"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fromValue = #(1.0f);
animation.toValue = #(0.1f);
animation.repeatCount = INFINITY;
animation.duration = 0.75;
animation.autoreverses = YES;
[label.layer addAnimation:animation];
When you click on your button, just get a pointer to that label and remove all the animations:
[label.layer removeAllAnimations];
Try this it give you the ability to manage the animation repeat count if you replaced INFINITY with your repeat count
fadingLabel.alpha = 1.0;
[UIView beginAnimations:#"fadingLabel" context:nil];
[UIView setAnimationDuration:4.0];
[UIView setAnimationRepeatCount:INFINITY];
fadingLabel.alpha = 0.0;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView commitAnimations];
Wayne Hartman's Answer in Swift 4
let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 1
animation.toValue = 0.1
animation.duration = 0.75
animation.repeatCount = .infinity
animation.autoreverses = true
label.layer.add(animation, forKey: nil)
and
label.layer.removeAllAnimations()
I'm trying to animate (fade in/out) a UILabel and I'm using the following code:
float newAlpha = 0.0;
//TODO:Check if the previous animation has finished
if(answer.alpha==0.0) {
newAlpha = 1.0;
} else if(answer.alpha==1.0) {
newAlpha = 0.0;
}
[UIView animateWithDuration:1.0 animations:^{
answer.alpha = newAlpha;
}];
Where the TODO comment is, I want to check if the previous animation has finished and if it hasn't, exit the method. Is there some way of doing this?
UPDATE #1:
you need a variable in your class:
BOOL _animationFinished;
and then you can use the following way for the animation:
float newAlpha = 0.0;
//TODO:Check if the previous animation has finished
if (_animationFinished == false) return;
if(answer.alpha==0.0) {
newAlpha = 1.0;
} else if(answer.alpha==1.0) {
newAlpha = 0.0;
}
[UIView animateWithDuration:1.0f animations:^{ answer.alpha = newAlpha; _animationFinished = false; } completion:^(BOOL finished){ _animationFinished = true; }];
it must be work.
ORIGINAL
I'm always checking the subject of the animation in this case, like this:
float newAlpha = 0.0;
//TODO:Check if the previous animation has finished
if (answer.alpha > 0.f || answer.alpha < 1.f) return; // it is always good enough for me
// ...or with AND it will cause the same effect:
// if (answer.alpha > 0.f && answer.alpha < 1.f) return;
if(answer.alpha==0.0) {
newAlpha = 1.0;
} else if(answer.alpha==1.0) {
newAlpha = 0.0;
}
[UIView animateWithDuration:1.0 animations:^{
answer.alpha = newAlpha;
}];
If you're using UIView then
[UIView setAnimationDidStopSelector:#selector(animationfinished)];
-(void) animationfinished
{
animationFinished = YES;
}
Use animateWithDuration:animations:completion: method to do your "previous animation", and set a flag in the completion handler to indicate if it's finished or not. Then, check the same flag exactly where you have the TODO comment.
Edit: Example below
-(void) animation1 {
// assume that alpha was 0 and we want the view to appear
[UIView animateWithDuration:1.0 animations:^{
answer.alpha = 1.0;
} completion:^(BOOL finished){
fristAnimationFinished = finished;
}];
}
-(void) animation2 {
float newAlpha = 0.0;
if (!firstAnimationFinished)
return;
if(answer.alpha==0.0) {
newAlpha = 1.0;
} else if(answer.alpha==1.0) {
newAlpha = 0.0;
}
[UIView animateWithDuration:1.0 animations:^{
answer.alpha = newAlpha;
}];
}
I know you can perform a two-stage animataion using blocks like so:
[UIView animateWithDuration:25.0 delay:0.0 options:UIViewAnimationCurveLinear animations:
^{
aView.alpha = 2.5;
}
completion:^(BOOL finished)
{
aView.hidden = YES;
}
];
..but how would I create a multistage (more than 2) animation using blocks?
Use nested animations:
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
//first animation
}
completion:^(BOOL finished){[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
//second animation
}
completion:^(BOOL finished){//and so on..
}];}];
or you can make a recursive, multi-stage animation method:
-(void) multiStageAnimate{
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
//animation code
}
completion:^(BOOL finished){
if(/* If terminating condition not met*/)
[self multiStageAnimate];
}];
}
I realize this is an older question, but I thought I'd add my input.
I created a class for handling multistage animation, available here!
It only supports a single duration and option set currently, but I'll probably add more features.
Here's how you use it:
// Create New Animation
MSAnimation * newAnimation = [MSAnimation newAnimationWithDuration:0.35 andOptions:UIViewAnimationOptionCurveEaseInOut];
// Add Sequence
[newAnimation addNewAnimationStage:^{
greenView.center = CGPointMake(greenView.center.x, greenView.center.y + 100);
}];
[newAnimation addNewAnimationStage:^{
greenView.center = CGPointMake(greenView.center.x + 100, greenView.center.y);
}];
[newAnimation addNewAnimationStage:^{
greenView.center = CGPointMake(greenView.center.x, greenView.center.y + 100);
}];
[newAnimation addNewAnimationStage:^{
greenView.center = CGPointMake(greenView.center.x - 50, greenView.center.y);
}];
[newAnimation addNewAnimationStage:^{
greenView.frame = CGRectMake(0, 0, 100, 100);
}];
// Animate Your Sequence With Completion
[newAnimation animateSequenceWithCompletion:^{
NSLog(#"All finished!");
}];
Gives you:
I want to let a UIImageView flash several times.
Currently I don't know how to do that.
Actual code:
-(void)arrowsAnimate{
[UIView animateWithDuration:1.0 animations:^{
arrow1.alpha = 1.0;
arrow2.alpha = 1.0;
arrow3.alpha = 1.0;
NSLog(#"alpha 1");
} completion:^(BOOL finished){
[self arrowsAnimate2];
}];
}
-(void)arrowsAnimate2{
[UIView animateWithDuration:1.0 animations:^{
arrow1.alpha = 0.0;
arrow2.alpha = 0.0;
arrow3.alpha = 0.0;
NSLog(#"alpha 0");
} completion:^(BOOL finished){;}];
}
later on I call it like this:
for (int i = 0;i < 10; i++){
[self arrowsAnimate]; }
This gives me 10x alpha 1, and then 10x alpha 0. In the middle we see only one animation.
Any suggestions?
Thanks.
There is a simpler way to achieve a flashing animation using only 1 animation block:
aview.alpha = 1.0f;
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationOptionAutoreverse
animations:^ {
[UIView setAnimationRepeatCount:10.0f/2.0f];
aview.alpha = 0.0f;
} completion:^(BOOL finished) {
[aview removeFromSuperview];
}];
The trick is to use [UIView setAnimationRepeatCount:NTIMES/2]; *_inside_* your animation block.
No need for extra functions or extra loops.
Use
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
and pass the UIViewAnimationOptionRepeat and probably UIViewAnimationOptionAutoreverse in your options. You shouldn't need to provide a completion block and only perform the first animation.
Edit: here is some sample code for an image that fades in and out indefinitely.
[UIView animateWithDuration:1.0
delay:0.0
options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse)
animations:^{
self.myImageView.alpha = 1.0;
}
completion:NULL];
Edit 2: I see you actually need to flash it 10 times only. I wasn't able to do that with blocks actually. When the completion block executed, the animation seemed to complete instantly the remaining 9 times. I was however able to do this with just the old-style animations quite easily.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:10.0];
[UIView setAnimationRepeatAutoreverses:YES];
self.myImageView.alpha = 1.0;
[UIView commitAnimations];
Edit 3: I found a way to do this with blocks.
- (void)animate
{
if (self.animationCount < 10)
{
[UIView animateWithDuration:1.0
animations:^{
self.myImageView.alpha = 1.0;
}
completion:^(BOOL finished){
[self animateBack];
}];
}
}
- (void)animateBack
{
[UIView animateWithDuration:1.0
animations:^{
self.myImageView.alpha = 0.0;
}
completion:^(BOOL finished){
self.animationCount++;
[self animate];
}];
}
Above blinking may not work when your app go background and foreground at time of blinking.
Instead of these you can take a transparent image and your actual image and animate your ImageView
UIImageView *imageview=[UIImageView new];
imageview.animationDuration=1;
imageview.animationImages = your array of images;
[imageview startAnimating];
You need to wait for an animation to complete before launching a new one. You could chain your completion block in animate2 to go back to animate, and stop based on a counter property, implementing your loop in the animate/completion blocks instead of a separate loop.