I've only worked a bit with UIView animations and blocks, basically one step animations. I'd like to stack a few steps into a sequence. The code below seems to be working but I'm wondering if this is the right approach and/or if there are any issues/limitations with placing blocks within blocks.
Blocks seem great though the code formatting gets sort of unwieldy/unreadable.
CGRect newFrame = CGRectMake(0, 0, 500, 500);
UIView *flashView = [[UIView alloc] initWithFrame:newFrame];
flashView.tag = 999;
[flashView setBackgroundColor:[UIColor grayColor]];
[flashView setAlpha:0.f];
[self.view addSubview:flashView];
[UIView animateWithDuration:.2f
animations:^{
// STEP 1: FADE IN
[flashView setAlpha:1.f];
}
completion:^(BOOL finished){
[UIView animateWithDuration:.9f
animations:^{
// STEP 2: FADE OUT
[flashView setAlpha:0.f];
}
completion:^(BOOL finished){
// STEP 3: CLEAN UP
[flashView removeFromSuperview];
}
];
}];
What you're doing is correct. Sometimes the nesting can get ugly. One alternative for readability is to put each animation in its own method:
-(IBAction)someButtonPressed:(id)sender {
[self saveSomeData];
[self fadeInAndOut];
}
-(void)fadeInAndOut {
[UIView animateWithDuration:.2f
animations:^{
// STEP 1: FADE IN
[self.flashView setAlpha:1.f];
}
completion:[self fadeOut]
];
}
-(void (^)(BOOL))fadeOut {
return ^(BOOL finished) {
[UIView animateWithDuration:.9f
animations:^{
// STEP 2: FADE OUT
[self.flashView setAlpha:0.f];
}
completion:[self cleanUpFlashView]
];
};
}
-(void (^)(BOOL))cleanUpFlashView {
return ^(BOOL finished){
// STEP 3: CLEAN UP
[self.flashView removeFromSuperview];
};
}
That way isn't horrible if you're just nesting once with simple code. If you're going to do something more complex you might try animateWithDuration:delay:options:animations:completion:
using delay: as a way to link the animations. For example:
[UIView animateWithDuration:.2f delay:0
options:UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionAllowUserInteraction
animations:^{
// STEP 1: FADE IN
[flashView setAlpha:1.f];
}
completion:nil
];
[UIView animateWithDuration:.9f delay:.2f
options:UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionAllowUserInteraction
animations:^{
// STEP 2: FADE OUT
[flashView setAlpha:0.f];
}
completion:^(BOOL finished){
// STEP 3: CLEAN UP
[flashView removeFromSuperview];
}
];
Related
Im trying to fade in and out of a light node I have created I just dont know where to call the function accordingly so it will continously fade in and out. Never tried animating anything here is what I have.
- (void)fadeOutIn:(UIView *)view duration:(NSTimeInterval)duration
{
[self enumerateChildNodesWithName:#"//*" usingBlock:^(SKNode *node, BOOL *stop) {
if ([node.name isEqualToString:#"light1"]) {
[UIView animateKeyframesWithDuration:5 delay:2 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse|UIViewAnimationCurveEaseInOut animations:^{
node.alpha = 0;
} completion:^(BOOL finished) {
}];
}
}];
}
where should I call this function?that is fades in and out continously?
Should it be called within an action?
Thank You
Use this for Continue fade in out.
-(void)viewDidAppear:(BOOL)animated{
[UIView animateKeyframesWithDuration:1 delay:0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse|UIViewAnimationCurveEaseInOut|UIViewAnimationOptionAllowUserInteraction animations:^{
node.alpha = 0;
} completion:^(BOOL finished) {
[UIView animateWithDuration:1 animations:^{
node.alpha = 1;
} completion:nil];
}];
}
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[objAnimate setAlpha:1.0];
[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear | UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse animations:^{
[objAnimate setAlpha:0.0];
} completion:nil];
}
This will create continuous fade in and out for your object when it appears. If you want to create programmatically a custom object and then use this code then do it after your custom object is added in your view as below (example):
objAnimate = [[UILabel alloc] initWithFrame:CGRectMake(20, 50, 200, 40)];
objAnimate.backgroundColor = [UIColor lightGrayColor];
[objAnimate setAlpha:1.0];
[self.view addSubview:objAnimate];
[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear | UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse animations:^{
[objAnimate setAlpha:0.0];
} completion:nil];
The view starts animating as soon as its added in your view.
I have a UIView which is initially hidden. I need to setHidden:NO (visible) with dropdown effect...
There's my simple code without effect
-(IBAction)btnAbrirDestaquesClick:(id)sender {
[self.viewDestaques setHidden:NO];
}
A simpler alternative to UIDynamicAnimator in iOS 7 is Spring Animation (a new and powerful UIView block animation), which can give you nice bouncing effect with damping and velocity:
[UIView animateWithDuration:duration
delay:delay
usingSpringWithDamping:damping
initialSpringVelocity:velocity
options:options animations:^{
//Animations
[self.viewDestaques setHidden:NO];
}
completion:^(BOOL finished) {
//Completion Block
}];
If you simply want to animate it try something like this:
[UIView animateWithDuration:.5 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.viewDestaques.frame = CGRectMake(0, 0, 320,30);
} completion:^(BOOL finished) {
[UIView animateWithDuration:.5 delay:2.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.viewDestaques.frame = CGRectMake(0, -30, 320,30);
} completion:^(BOOL finished) {
}];
}];
It worked for me:
-(IBAction)btnAbrirDestaquesClick:(id)sender {
[self.viewDestaques setTranslatesAutoresizingMaskIntoConstraints:YES]; //respeita o frame que eu setar, independentemente das constraints
[self.viewDestaques setFrame:CGRectMake(self.viewDestaques.frame.origin.x, self.viewDestaques.frame.origin.y, self.viewDestaques.frame.size.width, 0)];
[self.viewDestaques setHidden:NO];
while (self.viewDestaques.frame.size.height < self.frameViewDestaquesOriginal.size.height) {
[UIView animateWithDuration:2.0 animations:^{
[self.viewDestaques setFrame:CGRectMake(self.viewDestaques.frame.origin.x, self.viewDestaques.frame.origin.y, self.viewDestaques.frame.size.width, self.view.frame.size.height + 10)];
}completion: ^(BOOL completed){
}];
}
}
I'm having a bit of trouble. I've set my UIView Animation to repeat itself infinitely, however it does this before the animation starts it's completion function. I want it to wait until after the completion function fully runs because I want to run multiple animations. Any idea how to do this? If not is there a different way I can accomplish the same thing?
I'll post my code below:
-(void) createBlueControlAnimation:(int)messageCount{
if(messageCount == 0) {
return;
}
[blueControl setContentOffset:CGPointMake(0, 0)];
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionRepeat
animations:^{
[blueControl setContentOffset:CGPointMake(screenWidth, 0)];
} completion:^(BOOL finished){
if(messageCount >= 2){
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionAllowAnimatedContent
animations:^{
[blueControl setContentOffset:CGPointMake(screenWidth * 2, 0)];
} completion:^(BOOL finished){
if(messageCount == 3) {
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionAllowAnimatedContent
animations:^{
[blueControl setContentOffset:CGPointMake(screenWidth * 3, 0)];
}completion:^(BOOL finished){
[self scrollAnimationToStart];
}];
}else{
[self scrollAnimationToStart];
}
}];
}else{
[self scrollAnimationToStart];
}
}];
}
-(void) scrollAnimationToStart {
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionAllowAnimatedContent
animations:^{
[blueControl setContentOffset:CGPointMake(0, 0)];
} completion:^(BOOL finished){}];
}
So based on the number of messages I want the animation to continue running, and then when it gets to the last message, loop back to the first message and then restart the animation. However right now it just loops infinitely between the first and the second message. Any ideas on how to fix this would be greatly appreciated, thanks.
Firstly: You should create separate consts for anything that is in code.
extern NSTimeInterval const animationDuration;
NSTimeInterval const animationDuration = 0.5;
extern NSTimeInterval const animationDelay;
NSTimeInterval const animationDelay = 2;
Secondly: intent your code in reasonable way, it's quite hard to read what you've pasted. Use one method and push every repeatable part of code into another method.
- (void)createBlueControlAnimation:(NSUInteger)messageCount {
if (messageCount == 0) {
return;
}
__weak typeof(self) weakSelf = self;
[self offsetBlueControl:0];
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionAllowAnimatedContent animations:^{
[weakSelf offsetBlueControl:screenWidth];
} completion:^(BOOL finished) {
if (messageCount < 2) {
[self scrollAnimationToStart];
} else {
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionAllowAnimatedContent animations:^{
[weakSelf offsetBlueControl:screenWidth * 2];
} completion:^(BOOL finished) {
if (messageCount != 3) {
[self scrollAnimationToStart];
} else {
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionAllowAnimatedContent animations:^{
[weakSelf offsetBlueControl:screenWidth * 3];
} completion:^(BOOL finished) {
[self scrollAnimationToStart];
}];
}
}];
}
}];
}
- (void)scrollAnimationToStart {
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionAllowAnimatedContent animations:^{
[blueControl setContentOffset:CGPointMake(0, 0)];
} completion:^(BOOL finished) {}];
}
- (void)offsetBlueControl:(CGFloat)xOffset {
[blueControl setContentOffset:CGPointMake(xOffset, 0)];
}
I should mention here that this animation block is in fact similar everywhere. I'd set it as a separate method also.
Thirdly, keep same way of spacing everywhere (eg check out https://github.com/NYTimes/objective-c-style-guide).
That's all about code, the quality, readibility etc. I'd set it as comment, but there's too much code in my response, so it has to be an answer.
I don't fully understand what do you want to do. As far as I understood:
You have a UI element, called BlueControl.
You want to move this element.
Basing on some counter you want to move your view by some width (btw I advise to switch to NSIntegers from ints and use unsigned option, so NSUInteger etc). The move steps:
3.1. Move button right during 0.5s
3.2. Wait 2 seconds if counter is large enough, otherwise end step 3
3.3. Increase counter, repeat step 3.
You want to repeat the process infinitely.
Is that right? I need to understand the problem to edit this answer and paste a full response here.
I have three UIViews that i want to animate in a certain order (It is supposed to look like a news ticker with three different labels moving from above then display and then move all the way to the outside of the view). Now this whole process is supposed to repeat infinitely.
This is the code i used:
[UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveLinear animations:^
{
[morningPrayerView setFrame:CGRectMake(0, 0, 320, 30)];
[morningPrayerView setAlpha:1.f];
}
completion:^(BOOL finished)
{
[UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveLinear animations:^{
[morningPrayerView setFrame:CGRectMake(0, 30, 320, 30)];
[morningPrayerView setAlpha:1.f];
} completion:^(BOOL finished) {
{
[UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveLinear animations:^{
[middayPrayerView setFrame:CGRectMake(0, 0, 320, 30)];
[middayPrayerView setAlpha:1.f];
} completion:^(BOOL finished) {
{
[UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveLinear animations:^{
[middayPrayerView setFrame:CGRectMake(0, 30, 320, 30)];
[middayPrayerView setAlpha:1.f];
} completion:^(BOOL finished) {
{
[UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveLinear animations:^{
[eveningPrayerView setFrame:CGRectMake(0, 0, 320, 30)];
[eveningPrayerView setAlpha:1.f];
} completion:^(BOOL finished) {
{
[UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveLinear animations:^{
[eveningPrayerView setFrame:CGRectMake(0, 30, 320, 30)];
[eveningPrayerView setAlpha:1.f];
} completion:^(BOOL finished) {
//this block is called when the animation is over
}];
} }];
} }];
} }];
} }];
}];
The problem is that only the first animation gets the go and none of the other animations start.
IMHO the UIViewAnimationOptionRepeat switch means to repeat the animation indefinitely, therefore your first animation loops forever and never calls the completion handler. Simply wrap the whole animation setup in method, remove the repeat switches and when the last completion handler is executed, call the method again to repeat the whole show? Something like this:
- (void) animateView {
[UIView animateWithDuration:5.0 animations:^{
// …
} completion:^(BOOL finished) {
[self animateView];
}];
}
That’s probably a retain cycle, but as long as you cancel the animation eventually, it should be OK.
I am stumped by a very simple task, I want a UIView animation to do ease in / ease out but it is always linear despite using the correct animationOption.
Here is the code which by all accounts should work, it's set in a UIViewControllerAnimatedTransitioning class but I have the same issue in a previous custom segue class;
[UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^(void) {
self.dimView.alpha = 1.0;
self.imageView.frame = self.destinationRect;
} completion:^(BOOL finished) {
[self.imageView removeFromSuperview];
[self.dimView removeFromSuperview];
[transitionContext completeTransition:YES];
}];
However, a spring animation with the following code works although I'd rather not do this.
[UIView animateWithDuration:0.8f delay:0 usingSpringWithDamping:0.7f initialSpringVelocity:2.0f options:UIViewAnimationOptionCurveEaseInOut animations:^(void) {
self.dimView.alpha = 1.0;
self.imageView.frame = self.destinationRect;
} completion:^(BOOL finished){
[self.imageView removeFromSuperview];
[self.dimView removeFromSuperview];
[transitionContext completeTransition:YES];
}];
I'm getting the same behaviour on the simulator and on an iPad2 test device.
Am I doing something wrong?
Are there issues with animating either frame or alpha values?
You could try this instead.-
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.4f];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop)];
self.dimView.alpha = 1.0;
self.imageView.frame = self.destinationRect;
[UIView commitAnimations];
- (void) animationDidStop {
[self.imageView removeFromSuperview];
[self.dimView removeFromSuperview];
[transitionContext completeTransition:YES];
}
Hope it helps.
You can use the following :
[self transition:left]; //Call as required
enum animationFrom {
top,
bottom,
left,
right
};
- (void)transition:(NSUInteger)index {
CATransition *tr=[CATransition animation];
tr.duration=0.45;
tr.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
tr.type=kCATransitionPush;
tr.delegate=self;
switch (index) {
case top:
tr.subtype=kCATransitionFromBottom;
break;
case bottom:
tr.subtype=kCATransitionFromTop;
break;
case left:
tr.subtype=kCATransitionFromRight;
break;
case right:
tr.subtype=kCATransitionFromLeft;
break;
default:
break;
}
[self.view.layer addAnimation:tr forKey:nil];
}
The default animation curve option is UIViewAnimationOptionCurveEaseInOut, so you don't need to specify this one. It is UIViewAnimationOptionCurveLinear that you have to be explicit about.
Are you sure your animation curve is linear? Try animating two views side be side, with animation duration set to a few seconds.