How to dismiss a modal VC with fade out animation? - ios

I am using the following code in my presenting VC to fade in the child modal VC, and this works fine:
self.infoViewController.view.alpha = 0.0;
[self.navigationController presentModalViewController:self.infoViewController animated:NO];
[UIView animateWithDuration:0.5
animations:^{self.infoViewController.view.alpha = 1.0;}];
However I can't get it to fade out, I have tried a few things, this is the latest I tried that doesn't work:
- (IBAction)dismissAction:(id)sender
{
if ([[self parentViewController] respondsToSelector:#selector(dismissModalViewControllerAnimated:)])
{
[[self parentViewController] dismissModalViewControllerAnimated:YES];
self.parentViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
self.parentViewController.view.alpha = 0.0;
[UIView animateWithDuration:0.5
animations:^{self.parentViewController.view.alpha = 1.0;}];
} else
{
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
self.presentedViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
self.presentedViewController.view.alpha = 0.0;
[UIView animateWithDuration:0.5
animations:^{
self.presentedViewController.view.alpha = 1.0;}];
}
}
The modal view controller is faded out but immediately, not over a time period like it is when its displayed.

This (original part) is not to take away from H2CO3's correct answer. UIModalTransitionStyleCrossDissolve does pretty-much exactly the effect you're looking for. You are just not setting the modalTransitionStyle until it's to late. Replace all of your code with these functions in there respective positions:
-(void)show{
self.infoViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:self.infoViewController animated:YES];
}
- (IBAction)dismissAction:(id)sender{
[self dismissModalViewControllerAnimated:YES];
}
Edit in response to timing being an issue: Let's talk about the offending code. We'll concentrate on just the if true part, since it's essentially identical to the else.
[[self parentViewController] dismissModalViewControllerAnimated:YES];
self.parentViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
self.parentViewController.view.alpha = 0.0;
[UIView animateWithDuration:0.5
animations:^{self.parentViewController.view.alpha = 1.0;}];
If you're looking for a reciprocal animation this isn't it. In your original animation you set the next view's alpha to 0, then presented the next view controller, then set it's view's alpha to 1. So logically you need to dismiss the view controller after the animation; This is really easy using blocks. The code would look something like this:
[UIView animateWithDuration:0.5 animations:^{
self.view.alpha = 0;
} completion:^(BOOL b){
[self.presentingViewController dismissModalViewControllerAnimated:NO];
self.view.alpha = 1;
}];
This line of code animates the view's alpha to 0, then (upon completion) dismisses the presented view controller, and sets the view's alpha back to 1. This is a reciprocal animation.

In the docs of UIViewController, we can find:
#property(nonatomic, assign) UIModalTransitionStyle modalTransitionStyle
Set this property to UIModalTransitionStyleCrossDissolve and it will dissolve properly :)
Hope that helps.

I guess, this might be useful for those heroic guys, who still tries to use MPMoviePlayerViewController in full screen mode and with orientation, which differs from the app major one.
I've spent literally a half of working day playing with presenting MPMoviePlayerViewController modally or not modally. But there is no luck with that at all, in sense of transition animation changing. (The modal mode needed for setting orientation which differs from the app major orientation).
I tried presentViewController or presentModalViewController, but the result is the same. No matter what type is the modalTransitionStyle property set, if I do [... dismissViewControllerAnimated:true ...] then default transition style (UIModalTransitionStyleCoverVertical) used anyway. I tested that in iOS 5.1 device and in iOS 6 Simulator.
I haven't tried any other types of controllers... Considering that common controller has method dismissMoviePlayerViewControllerAnimated, I can assume, that this method is used anyway for dismissing a video controller, no matter how did it appear. (Entering transitions didn't work for me as well, except they did not process as CoverVertical (in case of modalTransitionStyle changing).
So, my solution was not use the transition animation at all.
I am sure, Apple had some reasons for allowing only some definite animation for MovieViewController (I really hope so, and that was made not because of "laziness"), but if they wanted some nice experience user would get, they failed, as in my app that's even better when the video appears without any animation (which is worse than CrossDisolving for sure) but that's better than banal CoverVertical.
Looking on that from developer's point of view, it really sounds like they spend much more money for designers to paint nice icons for customers instead of more pleasant and effective work of developers. (

Related

Animate image change of UIImageView when having an UIVisualEffectView above

My problem is as follows:
An UIImageView's view is going to be changed with an animation, like this:
[UIView transitionWithView:_backgroundArtworkImageView
duration:ANIMATION_DURATION
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
_backgroundArtworkImageView.image = blurredImage;
}
completion:nil];
It works perfectly fine as long as I don't have an UIVisualEffectView over the image view. If using the blur view on top, it results in no animation at all.
I've looked around for a bit and seen that snapshots of views could be something to look into, which also seems to be what Apple uses internally on iOS, for example when bringing up the app switcher; I'm not really sure how exactly to approach it though.
Got it working by taking a snapshot, append it to the superview, hide the real view, change it without animation and then fade the real view in with animation.
__block UIView *snapshot = [_backgroundArtworkImageView snapshotViewAfterScreenUpdates:NO];
[_artworkContainer insertSubview:snapshot belowSubview:_backgroundArtworkImageView];
_backgroundArtworkImageView.alpha = 0.0f;
_backgroundArtworkImageView.image = blurredImage;
[UIView transitionWithView:_artworkContainer
duration:ANIMATION_DURATION
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
_backgroundArtworkImageView.alpha = 1.0f;
}
completion:^(BOOL _) {
[snapshot removeFromSuperview];
snapshot = nil;
}];
Edit: I realized that the blur view was taking a small time to update, but to solve that a new snapshot of the resulting view could be taken and then animated with.

how to animate setting rootViewController for window

I set rootViewController for window by statement below. However, this statement is not animated.
self.appDelegate.window.rootViewController = navController;
Is it possible for me to animate this statement with the effect of NavigationController.popViewController?
Looks like I found the answer on another SO thread
[UIView transitionWithView:self.appDelegate.window
duration:.75
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.appDelegate.window.rootViewController = navController;
}
completion:nil];
However, it used dissolve effect. If someone could tell me which animation should I use to get the effect of popping from NavigationController.

iOS 8 Rotation shows back view

I am adding a view Controller to an existing one using addChildViewController and addSubview.
The problem is that when the device is rotating the back view controller is briefly visible. I am using notifications to detect a rotation and replace the presented view controller (landscape and portrait are on different scenes in the storyboard)
- (void) changeTheViewToPortrait:(BOOL)portrait andDuration:(NSTimeInterval)duration{
if(portrait){
self.landscapeViewiPad.hidden = YES;
self.portraitViewiPad.hidden = NO;
}else{
self.landscapeViewiPad.hidden = NO;
self.portraitViewiPad.hidden = YES;
}
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView commitAnimations];
}
I notice that this problem is only on iOs8.
Does anybody have an idea about what could be done to fix this.
If it's only about a UI issue you might just postpone the hiding of the original view a bit:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_USEC), dispatch_get_main_queue(),
^{
self.landscapeViewiPad.hidden = NO;
});
NSEC_PER_USEC is 1/1000s, that might be too short and you might need to try yourself what works for you.
If you reply "this is a hack" I would agree :)

Fading in a background on opening a view

Seems simple, but I can't get this to work with all the different methods I try.
All I want to do, is fade in an image from a black background when I open a new view controller.
So far I've tried this:
//I've set the image alpha to 0 on the storyboard already
- (void)viewDidLoad
{
[UIView animateWithDuration:5.0
delay:0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
myImage.alpha = 1;
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}
It runs, and the image comes up immediately (just doesn't seem to be animating!)
I know this is a simple question but I have already spent hours looking for a solution.
If anyone could help it would be much appreciated!
Thanks!
Animation should work from within -viewDidLoad but you may be doing something more in -viewDidLoad that is disrupting the animation.
or...
You could instead move your animation code to -viewDidAppear:
-(void)viewDidAppear:(BOOL)animated
{
//do the animation here
}

Choppy UIView animation

I use a UIView animation to randomly animate 5 squares (UIButtons) around the screen. Depending on a user selection, there are anywhere from 2 to 5 squares visible. When only 2 are visible, the other three's hidden values get set to YES, so they are actually still animating (right?), they just aren't visible. But when only 2 are visible, the animation is smooth, but when all five are visible, the animation gets choppy. I'm not really sure how to describe it, because the squares are still moving at the correct speed and moving to the correct points; the choppiness isn't terrible, just bad enough to be noticeable. Is there any way to get rid of it? This is the code I use to animate the squares:
Edit: changed animations to block:
[UIView animateWithDuration:animationSpeed
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
view.center = destPoint;
}
completion:^(BOOL finished){
if([view isEqual:squareThree])
[self moveBadGuys];
}
];
/*for(UIButton* button in squareArray) {
if(!shouldMove)
return;
[UIView beginAnimations:#"b" context:nil];
[UIView setAnimationDuration:animationSpeed];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
view.center = destPoint;
[UIView commitAnimations];
}*/
Edit: the view presenting this is the third in a stack of three UIViewController presented with
ViewController* controller = [[[ViewController alloc] init] autorelease];
[self presentModalViewController:controller animated:NO];
Does this way of presenting views eat up memory?
There are a few things that can cause this. It always comes down to how complex the content is. Also, simulator can be really bad about handling animation, so be sure you are testing on real hardware.
Are there large images on the buttons? Are the buttons casting shadows? Those things can slow it down.
Also- use block based animation. Not the old begin-commit methods.
Not exactly sure why it's slow, but have you tried nesting the thing differently?
if(!shouldMove)
return;
[UIView beginAnimations:#"b" context:nil];
[UIView setAnimationDuration:animationSpeed];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
for(UIButton* button in squareArray) {
view.center = destPoint;
}
[UIView commitAnimations];
does (almost - the logic is a bit different in the !shouldMove case, but that's a different story) the same, but in a cleaner way.

Resources