Pushing an MFMailComposeViewController onto the navigation stack? Not presented modally - objective-c

I have a table view, and in one of the cells, it says "contact". Upon selecting this cell, I'd like to push in a MFMailComposeViewController.
I can only seem to present this MFMailComposeViewController modally. What is the problem here?
Thanks!
Relevant code frag:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
//*works*//[self.navigationController presentModalViewController:controller animated:YES];
//*broken*//[self.navigationController pushViewController:controller animated:YES];
}
The error that I get is: " * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Pushing a navigation controller is not supported'
* Call stack at first throw:"
So it looks like I have a navigationController already, and since MFMailComposeViewController is a subclass of UINavigationController, I'm pushing a navController onto another navController?
I want my UI to be consistent, so I want to push a MFMailComposeViewController onto the nav stack rather than present it modally.

This is because MFMailComposeViewController isn't a subclass of UIViewController but of UINavigationController. UINavigationController throws an exception when you're attempting to push a UINavigationController or subclass of UINavigationController onto an existing stack. Presenting a UINavigationController modally is permitted.

According to Apple documentation
To display the view managed by this view controller, you can use any of the standard techniques for displaying view controllers
So what you are trying to do is supposed to work in both cases. Did you have a look at the logs ?
I would have bet your navigationController is nil, because this typically happens when you are using a plain UIViewController (not embedded in a UINavigationController, but it you actually present your modal view onto the navigationController, it may not be nil.

Related

iOS 7 NavigationController Segue Crash

I have a NavigationController as Initial View and a ViewController as the root view. From that root view i am trying to do a segue with:
[self.navigationController performSegueWithIdentifier:#"Segue" sender:self];
There´s another view controller in my storyboard and i gave the segue the correct identifier. On iOS 8 it works perfect, but on iOS 7 it crashes with the following message:
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Could not find a navigation controller for segue 'Segue'. Push segues can only be used when the source controller is managed by an instance of UINavigationController.'
I don´t get why it´s not working on iOS 7...
UPDATE: Here´s my Storyboard:
The problem is, that your segue has the UINavigationController as its source.
A UINavigationController should not perform a segue. It should only be connected to a rootViewController, which may be a UIViewController (or its subclass). A segue should be performed from the rootViewController of the UINavigationController. The segue should have the rootViewController as its source and some other UIViewController (or its subclass) as its destination.
Your screenshot shows that your UINavigationController is connected to a rootViewController. You now need to set the rootViewController as the source of the segue (having identifier Segue), which currently has your UINavigationController as its source. Now, from the rootViewController, call
[self performSegueWithIndentifier:#"Segue" sender:nil]
Do this:
[self performSegueWithIdentifier:#"Segue" sender:self];
use Self if you are already performing segue from within the rootViewController.
If not then try this .
[self.navigationController.viewControllers[0] performSegueWithIdentifier:#"Segue" sender:nil];
Hope this will help you.
I think you have no segue in storyboard from rootViewController to other viewController with name #"Segue".

return to ViewController after presentViewController

Im trying to return to a specific ViewController in it's current state after going from that ViewController to another using presentViewController.
But when I try to close the other ViewController (with dismissViewController) I get a white screen.
RootViewController *rootViewController
= [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil]
instantiateViewControllerWithIdentifier:#"RootViewController"];
[self presentViewController:rootViewController animated:YES completion:nil];
This isn't an option either because that instantiates a new viewcontroller and I want the old ViewController in its current state.
Do I need to pass the RootViewController as an argument when presenting the other ViewController or is there another option to return to the RootViewController in its current state?
Yes, there is a way to return original screen.
I met just like problem but solved it with following code line
[self dismissViewControllerAnimated:YES completion:nil];
one way to address this is to avoid having one view controller responsible for presenting and dismissing the other one.. what you can do is create a controller of controllers (give it a singelton method).. and have that object basically keep a reference to any view controller you are interested in maintaining its state. That way you wouldn't have worry about what's going on behind the scenes when you dismiss or present a view controller.

Is this the right way to add a view?

I make a program that shows a table.
If people click the search I will add another view covering the original view. The original view is [BNUtilitiesQuick listnewcontroller];
[[BNUtilitiesQuick window] addSubview:[BNUtilitiesQuick searchController].view];
[[BNUtilitiesQuick searchController] viewWillAppear:YES] is indeed called. So it seems that UIView has a pointer to it's controller
However, the view that the [[BNUtilitiesQuick listnewcontroller] viewWillDisappear] is not called
Moreover, [[BNUtilitiesQuick listnewcontroller] viewWillAppear] is also not called even when the user has finished modifying search term with this code:
[self.view removeFromSuperview];
I think I may be missing something here. What exactly should I do anyway so IOs knows that the searchController.view will be covering listNewController?
This is NOT the right way to do it. If the searchController is a full screen controller you should present it modally using presentViewController or push it onto the navigation stack as #StuR suggested.
In case your search view covers only part of the listnewcontroller you should use the containment API in iOS5.
Inside listnewcontroller (parent view controller) you would write:
[self addChildViewController:self.searchController];
[self.view addSubview:self.searchController.view];
[self.searchController didMoveToParentViewController:self];
For more in-depth information check out the WWDC 2011 session video "Implementing UIViewController Containment". Also watch "The Evolution of View Controllers on iOS" from 2012 because there are some changes and deprecations in iOS6.
ViewController *viewController = [[ViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
I'd consider using pushViewController for adding a full screen view. addSubview is for views that don't cover the entire screen.
viewWillDisappear and viewWillAppear will only me called if you pop or push the given viewController. You are simple adding a subview with it's own viewController inside(on top) of the present viewController. As StuR said, if you want to dismiss the current ViewController you should use:
BNUtilitiesQuick *searchController = [BNUtilitiesQuick alloc] init];
[self.navigationController pushViewController:searchController animated:YES];
You can read more about ViewControllers here: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007457

How to presentModalViewController without dismiss the TabBarController

Hey guys i`m trying to present a modal view controller inside an application with a tab bar controller. The problem is, every time the new view is presented, it on top of my tab bar.
I need to keep my tab bar even when the view is presented. Like google maps application does with his toolbar at the bottom of the screen.
How can i do that?
Thank you
By default, a modal view controller is meant to take up the entire screen (on an iPhone/iPod, at least). Because of this, it covers whatever you have on screen at the time.
A view controller presented via modal segue is meant to live on its own. If you want to keep your Navigation and TabBar, then just use a push segue to present the new ViewController. Remember to use this kind of segue, your presenting controller needs to be part of a UINavigationController already.
Use this to push a ViewController. If it is a UINavigationController it will push its linked RootViewController by itsself.
Create a viewController to push: (Using Storyboard)
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
or (Using Code/Nibs)
LoginViewController *viewController = [[LoginViewController alloc] init]; //initWithNibNamed in case you are using nibs.
//in case you want to start a new Navigation: UINavigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
and push with:
[self.navigationController pushViewController:vc animated:true];
Also, if you are using Storyboards for the segues you can use this to do all the stuff. Remember to set the segue identifier.
[self performSegueWithIdentifier:#"pushLoginViewController" sender:self]; //Segue needs to exist and to be linked with the performing controller. Only use this if you need to trigger the segue with coder rather than an interface object.
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"pushLiftDetail"]) {
[[segue.destinationViewController someMethod:]];
segue.destinationViewController.someProperty = x;
}
}
I think you'll need to add a UITabBar to the modal view and implement/duplicate the buttons and functionality that your main bar has. The essence of a modal window is it has total control until it is dismissed.
You might try putting your UITabBarController into a NavBarController, but I'm not certain that this will work.
UITabBarController -> NavBarController -> Modal View

Passcode ViewController Presentation from Modal View

I'm implementing a Passcode feature in my iPhone app which has a UITabBarController as a root view controller. I have everything working great in most situations, by displaying a modal Passcode ViewController from the tabBarController when the app goes into the background, like so:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if ([[NSUserDefaults standardUserDefaults] valueForKey:kPasscodeStringKey]) {
PasscodeEntryVC *passcodeView = [[PasscodeEntryVC alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:passcodeView];
[tabBarController presentModalViewController:nav animated:NO];
}
}
My problem comes when the app is already displaying a modal view controller when it enters the background. Then, no passcode view appears. What would be the correct way to do this? Instead of just sending the message to the tabBarController to present the view, should I be checking first to see what the current view is, then have that present the passcode? If so, how is this done? Thanks.
First - you are leaking memory because you do not release your passcodeView and navigation controller nav.
Second - you could keep a simple BOOL variable that is updated whenever a modal view is presented or dismissed. If there is a modal view, just call dismissModalViewController:animated: in your applicationDidEnterBackground: method.
You could also check the frontmost view controller with [self.navigationController.topViewController class], but I have found this to be unreliable.
What I usually do is to ensure that any views I have that may present a modal view controller to dismiss the modal view controller whenever it is sent the UIApplicationWillResignActiveNotification notification, while over in my app delegate, I set it up exactly like yours.
One caveat though, is that whenever you dismiss the said modal view controllers, you need to ensure that you dismiss them with animated: set to NO before presenting your passcode view controller.