how to know controller is presentModalViewController in or pushViewController in? - iphone

The view controller may got by pushViewController or presentModalViewController from super view controller. In this view controller how should I know which way the controller from? I need it to close the view --
if (presentModalViewController ) {
[self dismissModalViewControllerAnimated:YES];
}else{
[self.navigationController popViewControllerAnimated:YES];
}
thanks

self.navigationController will be nil if the view is not in a nav controller (for example because it was presented). Of course if you present and have a nav controller, you'll need to dig deeper.
Additionally "self.presentingViewController" may be nil if you were not presented.
Note a common idiom for iOS is to create a simple protocol to handle closing the view. The view that pushes/presents is the delegate and gets their "viewWantsToClose" target called. If you create a new Utility project, you'll see what I'm talking about. This is how Apple flips the "info" view closed. You could potentially let your view accept a block for the "doCloseAction".

You are responsible for pushing or presenting a view so you should know when to pop or dismiss a view. If you still have to perform this kind of check, you can verify from the child view if self.navigationController is nil or not

You can do:
if (self.navigationController == nil) {
//[self dismissModalViewControllerAnimated:YES]; //Deprecated in iOS 6.0
[self dismissViewControllerAnimated:YES completion:nil];
}else{
[self.navigationController popViewControllerAnimated:YES];
}
or
NSArray* views = [self.navigationController viewControllers];
if (self.navigationController == nil || self == [views objectAtIndex:0])
{
//In the root view of navigationController or a presented View.
[self dismissViewControllerAnimated:YES completion:nil];
}else{
[self.navigationController popViewControllerAnimated:YES];
}

Related

navigation bar is hidden when i return back to view in iOS

I am new in iPhone development. I created an app which use a navigation bar using storyboard. My problem is that i am opening a viewB programmatically from viewA on button click and it successful. Now to go back to viewA i have used cancel button. when i click on cancel button (previous) the (viewA) is opened but navigation bar is not shown. and viewA have navigation bar control but viewB does not.
Thanks in advance
View A
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
EditViewController *viewController = (EditViewController *)[storyboard instantiateViewControllerWithIdentifier:#"EditViewController"];
[self presentViewController:viewController animated:NO completion:NULL];
View B:
- (IBAction)cancelButtonPressed:(id)sender {
if ( lables != NULL) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ScannerViewController *viewController = (ScannerViewController *)[storyboard instantiateViewControllerWithIdentifier:#"ScannerViewController"];
[self presentViewController:viewController animated:NO completion:NULL];
}
else{
[self.navigationController popViewControllerAnimated:YES];
}
You are presenting the viewB & popping it using self.navigationController, you should use one way, either use presentviewcontroller & dismissviewcontroller.
[self dismissViewControllerAnimated:YES completion:nil];
For your scenario it is best to use UINavigationController
e.g
Pushing:
[self.navigationController pushViewController:viewController animated:YES];
Closing
[self.navigationController popViewControllerAnimated:YES];
You have to use a navigation controller, in your StoryBoard select your view A, Editor Menu > Embed In > Navigation controller.
http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/NavigationControllers.html
If you are using storyboard no need to present the viewController programmatically. press and hold ctrl, drag from viewCA to viewCB and select model from popup menu.
For dismissing the viewController
[self dismissViewControllerAnimated:YES completion:nil];
If you want to send any data to viewCB give segue identifier (for example "segid")
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"segid"])
{
//write your code here.
}
}
This method will get called automatically (delegate) while presenting (of pushing) viewController.
I was also facing the same problem. The solution is to never select the modal on click on the back button, because modal covers your whole view and that's why on the back screen navigation controller is not showing. So don't make connection for back button, just write the code for back button.
[self dismissViewControllerAnimated:YES completion:nil];
When you needed (e.g. viewDidAppear method) use,
[self.navigationController setNavigationBarHidden:NO animated:YES];

Navigating between 2 ViewControllers

Im using Navigation Controller for my ViewControllers,I set my importantViewController as something like this to be its RootView:
UINavigationController *navControl = [[UINavigationController alloc] initWithRootViewController: vc];
[self presentModalViewController: navControl animated: YES];
Then, I pushView anotherView the FrontViewController like this:
[self.navigationController pushViewController:vc animated:YES];
After a button is pressed in FrontViewController another view will be pushed ViewA but it is connected with another ViewController ViewB the same way as this AGAIN:
[self.navigationController pushViewController:vc animated:YES];
(Which I think Im doing wrong when dismissing either of them with [self.navigationController popViewControllerAnimated:YES];)
This is an illustration:
My problem is, I need to navigate between View A and View B then when I dismiss either of them it will got back to FrontViewController. Like a child of a child View. Thanks.
I think this is for dismissModalViewController, but try this,
From View B write code like this
[[self parentViewController].navigationController popViewControllerAnimated:YES];
and From View A you can write,
[self.navigationController popViewControllerAnimated:YES];
Or either you can use this,
[self.navigationController popToViewController:frontViewController animated:YES];
UPDATE
for (UIViewController *tmpController in [self.navigationController viewControllers])
{
if ([tmpController isKindOfClass:[FrontViewController class]])
{
[self.navigationController popToViewController:tmpController animated:YES];
break;
}
}
This is the best solution to achieve this.
Write this code on both of your View A or B.
Hope it works now :-)
There is one way #Prasad G indicated. But problem with this solution is you need the same object of frontViewController. You can't do this with creating a new object. For going to this way declare frontViewController object in appdelgate and while pushing it from importantVC use
appdelgate.frontViewController = // initialize
// Push it
While going back from view B
[self.navigationController popToViewController:appdelegate.frontViewController animated:YES];
Another solution is
for (UIViewController *vc in [self.navigationController viewControllers]) {
if ([vc isKindOfClass:[FrontViewController class]]) {
[self.navigationController popToViewController:vc animated:YES];
break;
}
}
Using this way you can go on any of view controller from any level of navigation stack.
Using the first solution if you have 10 view Controllers and you want to go on any of one so you have to first create object of all 10 View Controller in appdelegate.
This code may have spell issues as I just typed this here
Hope this helps :)
UPDATE
->You have impVC as your root view
-> You pushed frontVC
-> From there you Pushed VC_A
-> From there you want to push VC_B
so you are done with pushing and for coming back to VC_A you can use
[self.navigationController popViewControllerAnimated];
Now you can again come on VC_B and again pop it. For going to frontVC from VC_A you can use popViewControllerAnimated and for going to frontVC from VC_B you can use the for loop i mentioned.
Please explain if you are looking anything else. If you are still facing issue please explain.
[self.navigationController popToViewController:frontViewController animated:YES];
Try like this i think it will be helpful to you.
In FrontViewController After a button is pressed:
ViewA instance
[self.navigationController pushViewController:ViewA animated:YES]
When ViewA disissmed
[self.navigationController popViewControllerAnimated:YES];
Load ViewB in ViewA
ViewB instance
[self.navigationController pushViewController:ViewB animated:YES]
On back viewB to FrontViewController
FrontViewController instance
[self.navigationController popToViewController:FrontViewController animated:YES];
On back viewB to viewA
[self.navigationController popViewControllerAnimated:YES];

Dismiss ModalView does not work here

So I have a tabBarController as a modalview, and it shows up fine. As I click some of the tabs, the views are loading properly. I want to dismiss the modalView when I click on tabBarController.selectedIndex ==4
So I write in the viewDidLoad and also tried in the viewWillAppear of that view controller to dismissModalViewController and it does not work.
I tried
[self.parentViewController dismissModalViewControllerAnimated:YES];
// ... And also //
[self dismissModalViewControllerAnimated:YES];
Could someone point out why it does not work ?
All you have to do is pass a reference to the modally presented VC pointing on the VC that will present it modally.
Define a weak reference as a property in the UITabBarController subclass, and send a message to dismiss it when required.
For example using a property named mainViewController :
MySubclass *tbController = [[MySubclass ....];
tbController.mainViewController = self;
[self presentModalViewController:tbController animated:YES];
Then in MySubclass define
#property(assign) UIViewController *mainViewController;
and synthesize it, then when the tab you want gets selected :
[self.mainViewController dismissModalViewControllerAnimated:YES];
I think the 4th view controller (of the tab bar controller) is trying to get dismissed by the line
[self.parentViewController dismissModalViewControllerAnimated:YES];
Since this 4th view controller was not presented by any controller, this wont work.
And it is dismissing it's modal view controller by the line
[self dismissModalViewControllerAnimated:YES];
Since, this 4th view controller did not presented any view controller, this again should not work.
You want to dismiss the tab bar controller and not its 4th view controller.
Basically, you can get the reference of tab bar controller from the 4th view controller.
As, [yourFourthViewController.tabBarController.parentViewController dismissModalViewControllerAnimated:YES];
I am guessing this without actually trying. Let me know if this works.
If you have the UINavigationController as the parent controller then the following line will work for you.
[self dismissModalViewControllerAnimated:YES];
But here I think you have the UIViewController is the parent controller instead of the UINavigationController. So, You can do one thing when presentModalViewController.
if(objView == nil)
objView = [[YourViewController alloc] initWithNibName:#"YourViewController" bundle:nil];
UINavigationController *navigationController1 = [[UINavigationController alloc] initWithRootViewController:objView];
[self presentModalViewController:navigationController1 animated:YES];
Let me know if you need more help or any questions.

Crashing after dismissing a Modal View Controller

Im presenting and dismissing a Modal View Controller. I use delegation so I dismiss the modalView at the Parent.
- (void)launchDrawingSection{
drawingSectionViewController = [[DrawingSectionViewController alloc] init];
drawingSectionViewController.modalTransitionStyle = UIViewAnimationTransitionFlipFromLeft;
drawingSectionViewController.drawingModalDelegate = self;
[self presentModalViewController:drawingSectionViewController animated:YES];
}
- (void)didDismissDrawingModalView{
NSLog(#"didDismissDrawingModalView");
[drawingSectionViewController release];
[self dismissModalViewControllerAnimated:YES];
}
The app crashes after the dealloc method in the ModalView gets called.
Am I doing something wrong with the way I present and dismiss a Modal View Controller? Any idea?
Don't release before dismiss.
- (void)launchDrawingSection{
drawingSectionViewController = [[DrawingSectionViewController alloc] init];
drawingSectionViewController.modalTransitionStyle = UIViewAnimationTransitionFlipFromLeft;
drawingSectionViewController.drawingModalDelegate = self;
[self presentModalViewController:drawingSectionViewController animated:YES];
[drawingSectionViewController release];
}
- (void)didDismissDrawingModalView{
NSLog(#"didDismissDrawingModalView");
[self dismissModalViewControllerAnimated:YES];
}
Your fundamentals of Modal View Controller is not clear.
If you are using delegate protocols only to inform the drawing sections's parent control to dismiss the drawing section controller, then this is something useless. Because, the following thing does your job without use of delegates.
// Present drawing section.
- (void)launchDrawingSection{
drawingSectionViewController = [[DrawingSectionViewController alloc] init];
drawingSectionViewController.modalTransitionStyle = UIViewAnimationTransitionFlipFromLeft;
drawingSectionViewController.drawingModalDelegate = self;
[self presentModalViewController:drawingSectionViewController animated:YES];
[drawingSectionViewController release];
}
// (Put this in Drawing Section View Controller). This function dismisses drawing section.
- (void)dismissActionEvent{
// Drawing section view controller is asking its parent to dismiss it.
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
To understand clearly how presenting and dismissing modal view controller's work, refer to my answer here

'Tried to pop to a view controller that doesn't exist.'

I am getting this error when I call my method dismissView. Here is the method stub:
-(IBAction)dismissView
{
RootViewController *rootController = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:nil];
[self.navigationController popToViewController:rootController animated:YES];
}
That should work, and I've checked, rootController is initialized and allocated. Any ideas?
I had this problem recently and solved with something like this...
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
The -popToViewController is used to pop view controllers OFF the stack, down to one that already exists. Your UINavigationController has a stack of ViewControllers (stored in the viewControllers property), when you popToViewController, you're going to want to pass one of the elements in that array as the first argument.
What you most likely want to do in this case is use -popViewControllerAnimated:, which will remove the top ViewController from the stack
Swift 4
For anyone still looking for a better solution that doesn't involve the UINavigationController stack indexes, which is getting more problematic with bigger navigation stack - here's the easiest way to solve this:
if let destinationViewController = navigationController?.viewControllers
.filter(
{$0 is DestinationViewController})
.first {
navigationController?.popToViewController(destinationViewController, animated: true)
}
You're allocating the RootViewController right there. It does not exist in the navigation controller's stack, so no matter how far you pop, you won't reach it.
If you are using Storyboads, use this segue:
#import "PopToControllerSegue.h"
#implementation PopToControllerSegue
- (void) perform
{
UIViewController *sourceViewController = (UIViewController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *)self.destinationViewController;
for (UIViewController* controller in sourceViewController.navigationController.viewControllers) {
if ([controller isKindOfClass:destinationViewController.class]) {
[sourceViewController.navigationController popToViewController:controller animated:YES];
return;
}
}
NSLog(#"PopToControllerSegue has failed!");
}
#end
When using Push Segues you can easily go back to the root using this method:
[self.navigationController popToRootViewControllerAnimated:YES];
When using Modal Segues (because of the word dismiss in the question and as a general reference) you can dismiss the view controller using this method:
[self dismissViewControllerAnimated:YES completion:nil];
The UINavigationController has a stack of ViewControllers which is stored in the viewControllers(NSArray) property. Enumerate to the required ViewController and pop to that ViewController.
Following code should solve the problem.
-(IBAction)dismissView
{
NSArray *array = self.navigationController.viewControllers;
for (id controller in array) {
if ([controller isKindOfClass:[RootViewController class]]) {
[self.navigationController popToViewController:controller animated:YES];
}
}
}
Try this one line solution.
Swift 4+:
self.navigationController?.popToViewController ((self.navigationController?.viewControllers[1]) as! Your_ViewController, animated: true)
Objective-C:
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
In my case the problem was that I was in the root ViewController and this works only for VCs that are stacked over it.
To pop to the RootViewController use
navigationController?.popToRootViewController(animated: true)
self.navigationController?.present(viewControllers, animated: true, completion: nil)