UINavigationController pop several layers - ios5

I am building an app with the storyboard feature in ios5. I am using a navigation controller. In one situation, I drill down through several viewcontrollers: when I click the back button I would like to go directly to the root viewcontroller, instead of back through the chain of viewcontrollers. Is this possible?

Sure, in the view controller that you want to pop back, add this:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.navigationController popToRootViewControllerAnimated:animated];
}

Related

Go to first view controller in app

I need to go to the first view in my app. I have a few views pushed onto the stack then a modal navigation controller and more views pushed onto that.
The problem I'm having is that using [[self navigationController] popToRootViewControllerAnimated:YES]; only goes back to the first view in the modal stack.
And I can't get [[self navigationController] popToViewController:.. to work because the true first view controller isn't accesible with [[self navigationController] viewControllers].
Any ideas on how to accomplish this? Thanks.
Do this:
[[self navigationController] dismissModalViewControllerAnimated:YES];
That will get you back to the VC that modally presented the navigation controller. Getting farther back after that depend on how you pushed those "few views" before the navigation controller.
Edit - explanation to get to the deepest root...
It sounds like those "few views" are on another, underlying navigation controller's stack. This can be a little tricky, because the clean way to get farther back in that stack is to have that underlying navigation controller pop to it's own root. But how can it know that the modal VC on top of it is done?
Let's call the view controller that did the modal presentation of second navigation controller VC_a. It's a modally presented navigation controller whose topmost VC is VC_b. How can VC_a know to pop to it's navigation root when VC_b modally dismisses itself?
The good answer (usually) is that VC_b decided to dismiss itself for a reason - some condition in your app/model changed to make it decide to be done.
We want VC_a to detect this condition, too. When VC_b gets dismissed, and VC_a gets a viewWillAppear message because it's about to be uncovered:
// VC_a.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (/* some app condition that's true when VC_b is done */) {
// I must be appearing because VC_b is done, and I'm being uncovered
// That means I'm done, too. So pop...
[self.navigationController popToRootViewControllerAnimated:NO];
} else {
// I must be appearing for the normal reason, because I was just pushed onto the stack
}
}
You need to do it by using the delegation pattern. Specifically, by creating a protocol that implements the delegate's respondsToSelector method.
See this post for complete details. It should be almost exactly what you are looking for. I had to do something similar, except I only needed to pop one view off the navigation stack instead of using popToRootViewControllerAnimated:.
For iOS6...
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
In AppDelegate.m class create method with bellow flow...
-(void)MethodName{//your method name
YourViewController *objViewController = [[[YourViewController alloc] initWithNibName:#"YourViewController" bundle:nil] autorelease]; ///define your viewcontroller name like "FirstViewController"
UINavigationController *yourNavigationController = [[[UINavigationController alloc] initWithRootViewController:objViewController] autorelease];
self.window.rootViewController = yourNavigationController;
}
When you want redirect on firstview just call this method from appdelegate object....

I can came back to first navigation control page with a button?

-(IBAction) btnReturn:(id) sender{
firstView * firstview =[[firstView alloc]initWithNibName:#"firstView" bundle:nil];
[self.view pushViewController:firstview animated:NO];
}
with the previsly code I see the first view but the navigation control increment. I wold came bak as was the navigation starting point. Any help?
pushViewController:animated: will add to the navigation stack; you want popViewControllerAnimated: to go back one view in the stack.
If you want to return to the very first (root) view controller, you want popToRootViewControllerAnimated:.
See: https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UINavigationController_Class/Reference/Reference.html
In your above code you are pushing a controller on self.view (firstView, I am considering it as controller as you are creating it using initWithNibName: method, but you should you proper naming conventions to avoid confusions.) But view do not have any such method pushViewController:. Instead you sould use if you really have self (the controller in which you are using this IBAction) in navigation stack.
[self.navigationController pushViewController:firstview animated:NO];
To pop controller from navigation stack, follow what #gregheo suggested.
Try using popViewControllerAnimated instead:
- (IBAction)btnReturn:(id)sender
{
[self.navigationController popViewControllerAnimated:NO]; // Or Yes if you would like to have an animation.
}

Creating a TabBarControllerDelegate in a Storyboard

I'm having difficulty creating a UITabBarControllerDelegate in my Storyboard driven iOS5 application. Here is the situation:
I have an initial screen that will eventually handle login but which currently just has a button that sends the user to...
...a Tab Bar Controller with five tabs. Each of these tabs go to...
...a Navigation Controller with many child View Controllers under the root.
(If it helps, a screenshot of the relevant Storyboard section is here.)
When the user switches tabs, I always want the user to be directed to the Root View Controller for that particular Navigation Controller, and not the most recently visited View Controller (which is the default behavior).
I understand that to do so, I need to call popToRootViewControllerAnimated when a Tab is pressed as discussed here and here, but I can't figure out how to do that within the storyboard. How can I do this without scrapping the storyboard and starting over?
Thanks!
There are more than one solutions to your problem (its a matter of design pattern decision). Some of them could be:
Subclass UITabBarController and set it as the custom class of your tabbar in your storyboard (also connect the delegate to your object in order to be handled) and override the -tabBarController:didSelectViewController: delegate method
Pop to the root by calling -popToRootViewControllerAnimated from the viewWillDisappear event of every view that you want this behavior implemented
You can create your own TabBarController, implement a method that instantiate your view controllers
-(UIViewController*) viewControllerWithTabTitle:(NSString*) title
viewController(NSString *)viewController {
UIViewController* returnViewController = [self.storyboard
instantiateViewControllerWithIdentifier:viewController];
return returnViewController;
}
Then in the viewDidLoad method you create the array with the view controllers, that in your case would be the NavigationController's identifier that you set on the InterfaceBuilder.
- (void)viewDidLoad {
self.viewControllers=
[NSArray arrayWithObjects:
[self viewControllerWithTabTitle:#"Option 1" viewController:#"viewController1"],
[self viewControllerWithTabTitle:#"Option 2" viewController:#"viewController2"],
[self viewControllerWithTabTitle:#"Option 3" viewController:#"viewController3"],
[self viewControllerWithTabTitle:#"Option 4" viewController:#"viewController4"],
[self viewControllerWithTabTitle:#"Option 5" viewController:#"viewController5"], nil];
}

Navigate from one view to another using UIButton

I have an application with 2 views . In the first one I have a button which when I clicked the user should go to the second view. I tried what is explained before here from Karoley , but it does not work . When I click the button nothing happened?
Here is the code of my action :
-(IBAction)gotoSecondPage:(id) sender{
NSLog(#"In gotoSecondPage");
LeoActionViewController *aSecondPageController =
[[LeoActionViewController alloc]
initWithNibName:#"LeoActionViewController"
bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:aSecondPageController animated:YES];
}
LeoActionViewCOntroller is a controler for a second view.
It just do not switch to a second view. I do not know why
I put code your problem this will help you. First of all, you declare method and open .xib file and then connect to that button with selected touchupinside connection.
In the .h file:
- (IBAction)gotoSecondPage:(id) sender;
In the .m file:
- (IBAction)gotoSecondPage:(id) sender
{
NSLog(#"In gotoSecondPage");
LeoActionViewController *aSecondPageController =
[[LeoActionViewController alloc]
initWithNibName:#"LeoActionViewController"
bundle:nil];
[self.navigationController pushViewController:aSecondPageController animated:YES];
[aSecondPageController release];
}
I'm not sure in what capacity you want to switch views.
What immediately comes to mind is that you want a Navigation Controller. This is an object that lets you put view controllers on a stack and push and pop them to show and hide them. It creates a navigation pathway through your app and is easy to use. It also facilitates the 'standard' navigation bar which is found in many iphone apps.
If you just want to change one view for another view you can do many things including hiding and showing different views using setHidden:(bool)hidden. Otherwise you can use addSubview:(UIView *)view and removeFromSuperview to add and remove views completely from the superview.

viewWillAppear does not run when using addSubView!

I'm stuck! I can't see why viewWillAppear doesn't run in my code but viewDidLoad runs. If I understand it correctly viewDidLoad runs once on the first instance and viewWillAppear runs every time a view is added to the stack of views to display.
I see others have had this issue but some how their solutions of calling viewWillAppear directly causes my app to crash. Other solutions were related to Navigation Controller and pushingView's but thats not what i'm using either! What am I missing?
Thanks in advance for your help! :)
See below:
View Controller #1 - Currently being displayed on screen
-(IBAction)someButtonPressed:(id)sender{
NSLog(#"FirstViewController - someButtonPressed");
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondViewController.myLocation = self.myLocation;
secondViewController.myDatabase = self.myDatabase;
[self.view addSubview:secondViewController.view];
//[secondViewController viewWillAppear:YES];
}
SecondViewController:
- (void)viewWillAppear:(BOOL)animated {
NSLog(#"SecondViewController - viewWillAppear");
[super viewWillAppear:animated];
// updating ivars with data
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSLog(#"SecondViewController - viewDidLoad");
[super viewDidLoad];
}
If I understand it correctly viewDidLoad runs once on the first instance and viewWillAppear runs every time a view is added to the stack of views to display.
-viewDidLoad is called every time a UIViewController's view is loaded. That may be many times during a single controller's life as the view may be unloaded to free up memory when it is not visible and reloaded, triggering another call to -viewDidLoad, when needed.
-viewWillAppear: is called when a UIViewController's view becomes visible. However UIKit assumes that UIViewController's views will fill their window. Nesting UIViewControllers' views is an example of abusing UIViewControllers and will result in unexpected behavior. As you have seen.
See About Custom View Controllers in the View Controller Programming Guide for iOS:
Each custom view controller object you create is responsible for managing all of the views in a single view hierarchy. In iPhone applications, the views in a view hierarchy traditionally cover the entire screen, but in iPad applications they may cover only a portion of the screen. The one-to-one correspondence between a view controller and the views in its view hierarchy is the key design consideration. You should not use multiple custom view controllers to manage different portions of the same view hierarchy. Similarly, you should not use a single custom view controller object to manage multiple screens worth of content.
If you wrote a custom UIViewController Container you might have overwritten the following method, which leads to your described behavior.
- (BOOL)shouldAutomaticallyForwardAppearanceMethods{
return NO;
}
In this case you have to manually handle beginAppearanceTransition/endAppearanceTransition.
See Apples View Controller Containment article
viewWillAppear: is called when a view controller is displayed in one of the normal ways (e.g. by selecting a tab in a UITabBarController, by pushing onto a UINavigationController, by being popped back to in a UINavigationController, by being presented with presentModalViewController:animated, by being uncovered after dismissModalViewControllerAnimated:, etc). Just displaying a view with addSubview: does not call the method.
It is possible to correctly call viewWillAppear: manually, but in general it's better to use one of the normal ways mentioned above.
Just try this.. I got it working :)
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"SecondViewController - viewWillAppear");
// updating ivars with data
}
When you push view or present a view controller by pushViewController:animated or presentModelViewController:animated:, they will call viewWillAppear:animated:, and else method for you. But if you addSubview: manually, you need to call those method by self.