I have several animation blocks, all of which follow this basic format with different delays so that they fire one after another:
[UIView animateWithDuration:.85 delay:3 options:opts animations:[animations objectAtIndex:ww] completion:[completions objectAtIndex:ww]];
The options are just UIViewAnimationOptionAutoreverse in a variable for easy access.
I want there to be a delay between the animations and completion so that the images stay in their new position for a little bit before returning to the original one. I've considered using several simpler animateWithDuration:animations: blocks but I didn't see any way to do that with the delay in the documentation, unless I'm missing something.
#Paul.s here's the code I used with what you gave me:
void (^completion)(void) = ^{
[UIView animateWithDuration:.5
delay:5
options:UIViewAnimationCurveLinear
animations:[completions objectAtIndex:ww]
completion:^(BOOL finished) {}];
};
// Call your existing animation with the new completion block
[UIView animateWithDuration:.5
delay:1
options:UIViewAnimationCurveLinear
animations:[animations objectAtIndex:ww]
completion:^(BOOL finished) {
completion();
}];
for reference, the animation is super simple, just moving an image from one point to another and then back. the point at which it crashes is the [UIView animateWithDuration:.5 line where the completion block is defined, and it crashes after the first part of the animation runs.
How about passing another animation to the completion?
Updated
I have updated the code to be the exact working code from the sample I set up. This is using a clean project set up using the Empty Application template
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
CGRect startFrame = CGRectMake(0, 0, 100, 100);
UIView *view = [[UIView alloc] initWithFrame:startFrame];
view.backgroundColor = [UIColor greenColor];
[self.window addSubview:view];
// Set up your completion animation in a block
void (^completion)(void) = ^{
[UIView animateWithDuration:0.5f
delay:0.5f
options:UIViewAnimationCurveLinear
animations:^{
view.frame = startFrame;
}
completion:nil];
};
// Call your existing animation with the new completion block
[UIView animateWithDuration:4
delay:1
options:UIViewAnimationCurveLinear
animations:^{
view.frame = CGRectMake(200, 200, 10, 10);
}
completion:^(BOOL finished) {
completion();
}];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Related
How to achieve fade effect when going from one viewController to other using navigation Controller
normally we do to push :
DetailViewController *obj= [[DetailViewController alloc]initWithNibName:#"DetailView" bundle:nil];
[self.navigationController pushViewController:obj animated:YES];
and to pop :
UINavigationController *navController = self.navigationController;
// retain ourselves so that the controller will still exist once it's popped off
[[self retain] autorelease];
// Pop this controller and replace with another
[navController popViewControllerAnimated:YES];
But I want fading effect when I do pop or push .. plz suggest
this is not the exact code you want but you can get the way how to do the animation you want
First have to pass the parameter NO for any animation while push and pop view and have to give some custom animation like this
// Flash the screen white and fade it out to give UI feedback that a still image was taken
UIView *flashView = [[UIView alloc] initWithFrame:[[self videoPreviewView] frame]];
[flashView setBackgroundColor:[UIColor whiteColor]];
[[[self view] window] addSubview:flashView];
[UIView animateWithDuration:.4f
animations:^{
[flashView setAlpha:0.f];
}
completion:^(BOOL finished){
[flashView removeFromSuperview];
}
];
For more detail answer and use the block in proper way see this answer on other question on SO
Some of the code from that answer is as follow
For Push:
MainView *nextView = [[MainView alloc] init];
[UIView animateWithDuration:0.75
animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[super pushViewController:nextView animated:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];
}];
For Pop:
[UIView animateWithDuration:0.75
animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:transition forView:self.navigationController.view cache:NO];
}];
[self.navigationController popViewControllerAnimated:NO];
Thanks to #ijordan
Some other tips for the problem is here
This one is one excellent example for your use no use to do extra coding with this as it provides category for animation of navigation controller.
I rewrote The iOSDev's solution (thanks!) in Swift 5+ (without deprecation warnings etc.):
Push:
UIView.transition(with: navigationController.view, duration: 0.5, options: [.transitionCrossDissolve], animations: {
self.navigationController.pushViewController(viewControllerToBeShown, animated: false)
}) { _ in
completion()
}
Pop:
UIView.transition(with: navigationController.view, duration: 0.5, options: [.transitionCrossDissolve], animations: {
self.navigationController.popViewController(animated: false)
}) { _ in
completion()
}
I'm trying to add a short delay to my app. It's just the splash screen fade out so it won't affect any other functionality in the app (since nothing else is running). I've tried a variety of approaches with no success. (This is happening in viewDidLoad of a viewController):
C's sleep:
...
//add the splash screen
[self.view addSubview:splashScreen];
sleep(3);
[self fadeOut:splashScreen];
NSObject's performSelector (thought this would work because doesn't UIViewController inherit from NSObject?)
[self performSelector:#selector(fadeOut:) afterDelay:3];
NSTimeInterval:
//wait 3 seconds
NSTimeInterval theTimeInterval = 3;
[self fadeOut:splashScreen withADelayOf:&theTimeInterval];
Here is fadeOut (written to work with the NSTimeInterval example)
- (void) fadeOut:(UIView *)viewToToggle withADelayOf:(NSTimeInterval* ) animDelay {
[UIView setAnimationDelay:*animDelay];
[UIView animateWithDuration:0.25 animations:^{
viewToToggle.alpha = 0.0;
}];
}
I get the fadeOut but not the delay. Can someone nudge me in the right direction. Thanks.
you can try dispatch_after or animateWithDuration:delay:options:animations:completion:
double delayInSeconds = 0.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//yourcode
});
or
[UIView animateWithDuration:0.175 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
//your code
}completion:^(BOOL completed){
//animation completion execution
}];
You should rely on this method if you want to animate some properties of your view with some delay:
+(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
Have a look to the ref doc here.
So your code could be something like this:
[UIView animateWithDuration:0.25 delay:3.0 options: UIViewAnimationOptionCurveLinear animations:^{
viewToToggle.alpha = 0.0;
} completion: nil];
Tiguero and J2TheC both pointed me in the exact direction I needed to go:
Here's the code I used in case someone else needs the assist:
//add the splash screen
[self.view addSubview:splashScreen];
//fade it out with a delay
[UIView animateWithDuration:0.75 delay:3.0 options:UIViewAnimationOptionCurveEaseIn
animations:^{
splashScreen.alpha = 0.0;
}
completion:^ (BOOL finished) {
//do something on end
}];
Do One thing:
Make an uiimageview ref in the Appdelegate and in the didfinishLunching do this:
// Dont assign the main viewcontroller to the windwo here.
{
img = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"splash.png"]];
img.frame = CGRectMake(0, 0, 320, 480);
[self.window addSubview:img];
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(removeSplash) userInfo:nil repeats:NO];
[self.window makeKeyAndVisible];
}
- (void)removeSplash{
[img removerFromSuperview];
self.window.rootViewController = self.viewController;
}
I'm trying to animate a view with some buttons and controllers for my main view into the screen from the right side of the screen. The problem is it doesn't animate, it just goes directly to the finished state.
This is my code:
UIStoryboard* storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
options = [storyBoard instantiateViewControllerWithIdentifier:#"OptionsView"];
options.delegate = self;
[self.view addSubview:options.view];
options.view.center = CGPointMake(floor(total_width+options.view.frame.size.width/2)+10, floor(total_height/2));
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationOptionCurveEaseOut
animations:^{
options.view.center = CGPointMake(floor(total_width-options.view.frame.size.width/2)+10, floor(total_height/2));
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
EDIT
To make things more strange, if I put this animation inside the completion block of another animation then this one works, but not the new one.
UIButton* boton = (UIButton*) sender;
[UIView animateWithDuration:5.0
delay:0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
boton.alpha = 0.0;
//Jumps directly to 0, animation doesn't work
}
completion:^(BOOL finished){
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
options.view.center = CGPointMake(floor(total_width-options.view.frame.size.width/2)+10, floor(total_height/2));
//It works now, but it doesn't take 5 seconds to start, it starts right away
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}];
The line before the animateWithDuration:... call directly assigns the center to the same value that you're animating to. Either remove that or assign the initial center value there. An animation that has the same start and end values obviously has no effect.
I want to create an animated menu.
first it's hidden outside screen
when user taps the screen, it will slide in
user can tap it and it do something
then after 3s it will slide outside screen
But problem is it not response when I tap it. Why?
example code: All code is in my UIView class.
showing = NO;
box = [[UIView alloc] initWithFrame:CGRectMake(500,-200,200,200)];
box.backgroundColor = [UIColor redColor];
UITapGestureRecognizer *tapBox = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapMenuHandler:)] autorelease];
[box addGestureRecognizer:tapBox];
[self addSubView:box];
and:
-(void) tapMenuHandler: (UIView *)obj {
//Do something
NSLog(#"tap box");
}
and:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (!showing) {
box.frame = CGRectMake(500, -200, 200, 200);
[UIView animateWithDuration:0.5f delay:0.3f options:(UIViewAnimationOptionAllowUserInteraction) animations:^{
box.frame = CGRectMake(500, 200, 200, 200);
} completion:^(BOOL finished) {
showing = YES;
[UIView animateWithDuration:0.5f delay:3.0f options:(UIViewAnimationOptionAllowUserInteraction) animations:^{
box.frame = CGRectMake(500, -200, 200,200);
} completion:^(BOOL finished) {
showing = NO;
}];
}];
}
}
Thank for your help and sorry for my English :)
Edit: more detail
I tried set start my box's origin in screen (ex. 500,600), It still always response when tap at start point (500,600) even though my box move to another position.
Update:
I changed my way to move box outside screen by use NSTimer then it's work!
[UIView animateWithDuration:0.5f delay:0.3f options:UIViewAnimationOptionAllowUserInteraction animations:^{
box.frame = CGRectMake(500,200,200,200);
} completion:^(BOOL finished) {
isShowMenubar = YES;
//use NSTimer instead of delay
[NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:#selector(hideBox) userInfo:nil repeats:NO];
}];
and
-(void)hideBox {
[UIView animateWithDuration:0.5f delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{
box.frame = CGRectMake(500,-200,200,200);} completion:^(BOOL finished) {
isShowMenubar = NO;
}];
}
The reason why the box is not receiving touches has to do with how animation works. Your box is actually offscreen, but visually, it's going through the animation.
Even though you set the delay as 3.0, it still moves the box beforehand (due to the animation method still being called). So in your current code, if you tapped on the box at (500, -200) then it will receive the touch.
To fix this, either use an NSTimer or Grand Central Dispatch to delay.
NSTimer:
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self action:#selector(dismissBox) userInfo:nil repeats:NO];
The problem with NSTimer is that you'll have to put that animation code in a separate method.
Grand Central Dispatch:
double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[UIView animateWithDuration:0.5f animations:^{
box.frame = CGRectMake(box.frame.origin.x, -200, 200,200);
}completion:^(BOOL finished) {
showing = NO;
}];
});
I ran the code and didn't see the red view either. Then playing around for a bit, I got that your view is animating but is still out of bounds so you never see it. try changing the animation block to something like (the origin of the frame is at the left bottom corner)
box.frame = CGRectMake(100, 100, 200, 200);
and hopefully it will come animating.
Also, assuming that you run these in your view controller, the correct method should be
[self.view addSubview:box];
hope this helps
I want to animate my background after a view got loaded in my iOS application. This works with solid colors, but I'm not able to let it work using background images. This is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
[UIView animateWithDuration:5.0 animations:^{
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background.png"]];
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background_dark.png"]];
}];
[UIView commitAnimations];
}
Does anyone know the correct way to do this?
Thanks
You animate things like this:
view.backgroundColor = INITIAL COLOR
[UIView animateWithDuration:5.0 animations:^{
view.backgroundColor = FINAL COLOR
}];
The animate method animates the change from the current state to whatever you set up in the animation block. The animation block is not a list of steps.
Therefore, I think what you want is this:
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background.png"]];
[UIView animateWithDuration:5.0 animations:^{
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background_dark.png"]];
}];
Also, do NOT call commitAnimations afterward, that's only if you're using the old-style beginAnimations method. Read the documentation, and don't just call methods to see if they work.
Not all properties can be animated like this. It is very likely that a background image is one of them. What you can do is have a second view on top with the new image and animate its alpha from 0 to 1.
Ty this:
[UIView animateWithDuration:5.0 animations:^{
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background.png"]];
} completion:^(BOOL finished)
{
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background_dark.png"]];
}];