isBeingDismissed not set in viewWillDisappear: - iphone

I have some code to clean up in my viewWillDisappear:, which I only want to use when the view is moving back to the parent view controller.
- (void)viewWillDisappear:(BOOL)animated
{
if ([self isMovingFromParentViewController] || [self isBeingDismissed]) {
NSLog(#"isMovingFromParentViewController or isBeingDismissed");
// clean up
}
[super viewWillDisappear:animated];
}
The view can be presented in two ways: it can be pushed by a navigation controller, or presented as a modal view controller (from the same navigation controller). If it's pushed, then popped (pressing the back button), my clean-up code runs. If it it presented as a modal view controller, then dismissed, the code doesn't run.
I dismiss like so:
[rootViewController dismissModalViewControllerAnimated:YES];
My question is: why isn't isBeingDismissed set when I dismiss my view controller?

If this is the first view controller in a modal navigation controller that's being dismissed, calling self.isBeingDimissed() from viewWillDisappear: returns false.
However, since the entire navigation controller is being dismissed, what actually works is self.navigationController?.isBeingDismissed(), which returns true.

As #Yuval Tal mentioned, this flag does not work when you're dismissing controller that is embeded inside navigation controller. Here's an extension that I use:
extension UIViewController
{
var isAboutToClose: Bool {
return self.isBeingDismissed ||
self.isMovingFromParent ||
self.navigationController?.isBeingDismissed ?? false
}
}
It can be easily extended when you find another case when standard .isBeingDismissed won't work. And if you find, let us, let me know in comments.

Your issue is how you are dismissing your modal view. How is rootViewController being defined?
When I call [self dismissModalViewControllerAnimated:YES] then [self isBeingDismissed] evaluates to true.
When I call [parentViewController dismissModalViewControllerAnimated:YES] then [self isBeingDismissed] evaluates to true, whereby parentViewController is the UIViewController that presented the modal view (note: not a UINavigationController).

If by some chance you came here trying to use isBeingDismissed on a non-modally presented view controller, you can always check the topViewController property of your navigationController, for instance:
if navigationController?.topViewController != self {
return
}

viewController.isBeingPresented == NO;
[rootVC presentViewController:viewController animated:NO completion:^{
viewController.isBeingPresented == NO;
viewController.isBeingDismissed == NO;
[viewController dismissViewControllerAnimated:NO completion:^{
viewController.isBeingDismissed == NO;
}];
viewController.isBeingDismissed == NO; // is not work
}];
viewController.isBeingPresented == YES; // is work
viewController.isBeingPresented == NO;
[rootVC presentViewController:viewController animated:NO completion:^{
viewController.isBeingPresented == NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
viewController.isBeingDismissed == NO;
[viewController dismissViewControllerAnimated:NO completion:^{
viewController.isBeingDismissed == NO;
}];
viewController.isBeingDismissed == YES; // is work
});
}];
viewController.isBeingPresented == YES; // is work

Related

Force portrait in one view controller makes other to be in portrait initially

The root view controller of navigation controller supports only portrait orientation and other controllers supports all orientation.Now if i am on the root view controller and the DEVICES is in landscape and if i push next view controller that opens in portrait that should open in landscape as it supports all orientation.
Please help me with this.
Using iPhone 4s iOS6.1.3
you can check Device orientation in your first screen after login viewcontroller using bellow code:-
-(void)viewWillAppear:(BOOL)animated
{
[self willRotateToOrientation:[[UIDevice currentDevice] orientation]];
[super viewWillAppear:YES];
}
- (void)willRotateToOrientation:(UIInterfaceOrientation)newOrientation {
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation))
{
if (newOrientation == UIInterfaceOrientationLandscapeLeft || newOrientation == UIInterfaceOrientationLandscapeRight) {
//set your landscap View Frame
[self supportedInterfaceOrientations];
}
}
else if (UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation))
{
if(newOrientation == UIInterfaceOrientationPortrait || newOrientation == UIInterfaceOrientationPortraitUpsideDown){
//set your Potrait View Frame
[self supportedInterfaceOrientations];
}
}
// Handle rotation
}
sor when you load this viewcontroller it check first device oriantation and then load it's related frame
I think this is the issue related to the orientation changes in iOS6. You need to subclass the UINavigationController
Check this
1 . You have to create sub class of UINavigationController. add Following method.. Take one boolean variable to check whether it support for all orientation or not and change its value.
#implementation NavigationControllerViewController
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
AppDelegate *appdelgate=[[UIApplication sharedApplication]delegate];
if (appdelgate.issuppoertAll) {
// for iPhone, you could also return UIInterfaceOrientationMaskAllButUpsideDown
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationMaskPortrait;
}
#end
2 when you navigate form root view controller to other view controller
use this code , when you want to forcefully change its orientation.i.e lanscape to portrait
obj_viewcontroller = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[self presentModalViewController:obj_viewcontroller animated:NO];
[self dismissModalViewControllerAnimated:NO];
[self.navigationController pushViewController:obj_viewcontroller animated:NO];
3 In second view controller you have to change boolean variable value
-(void)viewWillAppear:(BOOL)animated
{
appdelgate.issuppoertAll=YES;
}
4 Add this method into all view controller and set orientation as per your need.
- (NSInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}

Dismissing camera view crashes app

When I try to dismiss my UIImagePickerController, it crashes the app. the error is: "Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'preferredInterfaceOrientationForPresentation must return a supported interface orientation!'"
I have the preferred interface orientation set in my view controller.
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
- (BOOL) shouldAutorotate {
return YES;
}
Here is the method I'm calling to bring up the camera, this works fine for adding the camera, but like I said, crashes when I try to remove the camera.
-(IBAction)addCamera:(id)sender
{
self.cameraController = [[UIImagePickerController alloc] init];
self.cameraController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.cameraController.cameraViewTransform = CGAffineTransformScale(self.cameraController.cameraViewTransform,
1.13f,
1.13f);
self.cameraController.showsCameraControls = NO;
self.cameraController.navigationBarHidden = YES;
self.wantsFullScreenLayout = YES;
ar_overlayView = [[UIView alloc] initWithFrame:CGRectZero];
self.view = ar_overlayView;
[self.cameraController setCameraOverlayView:ar_overlayView];
[self presentViewController:cameraController animated:NO completion:nil];
[ar_overlayView setFrame:self.cameraController.view.bounds];
}
-(IBAction)back:(id)sender
{
[ar_overlayView removeFromSuperview];
[cameraController dismissViewControllerAnimated:NO completion:nil];
}
Alright, found the solution, it was really simple, I just changed my back method to:
[self dismissModalViewControllerAnimated:YES];
Now my camera goes away and I can see my original view when I press the back button.
I still haven't figured out what was causing the original problem as I've gone through the info.plist and the methods for supported orientations, but this accomplishes what I wanted.
I'm still curious as to what was causing the error though if anyone has any ideas.
You can try remove this method:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
It will fix it.but sometimes it will lead to reduce stateBar height.
You cannot remove a UIViewController's main view from its superview.
Instead of this:
self.view = ar_overlayView;
Try this:
[self.view addSubview:ar_overlayView];
Then you will be able to remove it from the superview correctly.
You should be using the didFinishPickingMedieWithInfo method similar to below and use [self dismissModalViewControllerAnimated:YES];
-(void) imagePickerController:(UIImagePickerController *) picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
NSLog(#"Camera");
}
else {
NSLog(#"Album");
}
[self dismissModalViewControllerAnimated:YES];
}
I don't think you need to add ar_overlayView to the current view yourself if it's a camera overlay.
Here's what your code is doing now:
Add ar_overlayView to the current view
Add ar_overlayView as the camera's overlay view
Show the camera view (modal)
At this point, ar_overlayView is being displayed twice. When you send it the removeFromSuperview message on dismissing the camera view, it might be getting confused since it's in two view hierarchies at the same time.
Skipping the self.view = ar_overlayView; or [self.view addSubview:ar_overlayView]; lines should fix the problem.
Also, dismissViewControllerAnimated:completion: should be sent to the same object that presentViewController:animated:completion was called on (self in this case):
-(IBAction)addCamera:(id)sender
{
// -- snip -- //
ar_overlayView = [[UIView alloc] initWithFrame:self.cameraController.view.bounds];
[self.cameraController setCameraOverlayView:ar_overlayView];
[self presentViewController:self.cameraController animated:NO completion:nil];
}
-(IBAction)back:(id)sender
{
[self dismissViewControllerAnimated:NO completion:nil];
}

Strange warning dismissing modal view controller

I'm working on iOS 6.
My application has a standard navigation controller with embedded a CustomViewController.
In this controller I create a modal view like this:
-(IBAction)presentModalList:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
StationsListViewController *list = [storyboard instantiateViewControllerWithIdentifier:#"StationsListViewController"];
[list setStationsData: [self.stationsData allValues]];
[self presentModalViewController:list animated:YES];
}
The modal controller show perfectly but dismissing generates a warning.
The dismiss method in this controller is:
-(IBAction)backToMap
{
[self dismissModalViewControllerAnimated:YES];
}
The warning generated is Warning:
Attempt to dismiss from view controller < UINavigationController: 0x1ed91620 > while a presentation or dismiss is in progress!
Any clues about that?
Thanks
I realise this is a late answer but maybe this will help someone else looking for a solution to this, here is what I did:
-(IBAction)backToMap
{
if (![[self modalViewController] isBeingDismissed])
[self dismissModalViewControllerAnimated:YES];
}
For me, i found that line of code was being called multiple times, I couldn't find out why so this was the easiest fix.
Thanks JDx for getting me on the right track. I adapted it to form this solution, which will remove the warning without using functions that are deprecated in iOS 6:
-(IBAction)backToMap
{
if (![self.presentedViewController isBeingDismissed]) {
[self dismissViewControllerAnimated:YES completion:^{}];
}
}
Targeting iOS6, this is what worked for me:
if (![self.presentedViewController isBeingDismissed])
[self.presentedViewController dismissViewControllerAnimated:YES
completion:nil];
I found this approach to be unreliable - say one case in five I'd still see the error.
My solution was to use the completion block to set a flag which controls whether or not it's safe to dismiss - that way you don't need to check whether or not the view is being dismissed.
-(IBAction)presentModalView:(id)sender {
:
self.canDismiss = NO;
[self presentViewController:aVC animated:YES completion:^{
self.canDismiss = YES;
}];
:
}
In the bit of code where the dismiss occurs, just check the flag:
-(void)dismisser {
:
if (self.canDismiss) {
[self dismissViewControllerAnimated:YES completion:nil];
}
:
}
Hey presto, no more errors!
You can do whatever you want after the completion of the dismiss method as:
-(IBAction)backToMap
{
[self dismissViewControllerAnimated:YES
completion:^{
//Do something here
}];
}

dismissing modalViewController of modalViewController

So I have a UITabBarController app and I want to display a login page, and so I did:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(userDidLogin:) name:UserDidLoginNotification object:nil];
LoginViewController* loginViewController = [[LoginViewController alloc] init];
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:0];
[self.tabBarController.selectedViewController presentModalViewController:loginViewController animated:NO];
[loginViewController release];
Inside my LoginViewController I can as well show another modalViewController:
- (void) twitterLogin: (UIViewController *) askingView
{
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine: _twitter delegate: self];
if (controller) {
self.askingView = askingView;
[askingView presentModalViewController: controller animated: YES];
}
}
I have the following method where the askingView is the LoginViewController,
when I want to dismiss this I do:
[self.askingView dismissModalViewControllerAnimated:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:UserDidLoginNotification object:nil];
However, this doesn't dismiss the LoginViewController and show the UITabBarController views.. it just dismisses my modalViewController shown from the LoginvVIewController. What am I doing wrong here? I am also getting the following error:
attempt to dismiss modal view controller whose view does not currently appear. self = <LoginViewController: 0x2aff70> modalViewController = <SA_OAuthTwitterController: 0x2d2a80>
2011-09-16 09:45:37.750 VoteBooth[4614:707] attempt to dismiss modal view controller whose view does not currently appear. self = <MainViewController: 0x29fec0> modalViewController = <LoginViewController: 0x2aff70>
In order to dismiss a modal view that is presented over another modal view, you have to call dismissModalViewControllerAnimated: on the parent of the parent. I have used this in some of my apps and it has worked beautifully for me (after many painstaking hours trying to figure it out). Here is exactly what I've used:
[[[self parentViewController] parentViewController] dismissModalViewControllerAnimated:YES];
if ([self respondsToSelector:#selector(presentingViewController)]) {
[self.presentingViewController.presentingViewController dismissModalViewControllerAnimated:YES]; // for IOS 5+
} else {
[self.parentViewController.parentViewController dismissModalViewControllerAnimated:YES]; // for pre IOS 5
}
If you have a dynamic UX and do not know how many parents to go to, you can use this recursive function to figure it out...
- (void) goHome
{
//Dismiss modal back to home page
int numberOfVcsToDismiss = [self findRootViewController:self];
[self dismissToRootVc:numberOfVcsToDismiss];
}
- (int) findRootViewController:(UIViewController*)vc
{
if(vc)
{
return 1 + [self findRootViewController:vc.presentingViewController];
}
return 0;
}
- (void) dismissToRootVc:(int)count
{
if(count == 1)
[self dismissViewControllerAnimated:YES completion:^{}];
if(count == 2)
[self.presentingViewController dismissViewControllerAnimated:YES completion:^{}];
if(count == 3)
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:^{}];
if(count == 4)
[self.presentingViewController.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:^{}];
if(count == 5)
[self.presentingViewController.presentingViewController.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:^{}];
//etc....
}

Making one specific class of view controller auto rotate in a tab bar app, but forcing all other classes of view controller to stay portrait

I have a tab bar controller with this code
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
//NSLog(#"object type %#" ,nil);
if([[self navigationController ] isKindOfClass:[UINavigationController class]])
if([[[self navigationController] visibleViewController] isKindOfClass:[SLImageViewController class]])
return YES;
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
I need any instance of the SLImageViewController class to rotate, but none of the others. I have done everything i can think of like adding return YES to my SLImageViewController and other fixes.
Can anyone tell me what I'm doing wrong?
You could accomplish this by:
setting statusBar orientation to viewWillAppear and viewWillDisappear
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear: animated];
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight];
}
-(void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear: animated];
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait];
}
and rotating a view manually: self.view.transform = CGAffineTransformMakeRotation(M_PI/2);
presenting that view modaly will trigger shouldAutorotateToInterfaceOrientation method