I have an UINavigationController in my iPhone app controlling views in a drill-down fashion. In the second last view I'd like to return to the root view by pressing the default back button. I know that the method popToRootViewControllerAnimated: does just that. But where do I place it?
One option is to use leftBarButtonItem rather than backBarButtonItem on self.navigationItem. i.e.
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:self action:#selector(popToRootViewController:)];
Which would then call the selector popToRootViewController in your view controller, which could be implemented like so:
-(void)popToRootViewController:(id)sender {
[self.navigationController popToRootViewControllerAnimated:YES];
}
The issue that I see is that you won't have the native back button appearance.
Another option is to change the target and selector property on the backBarButtonItem.
[self.navigationItem.backBarButtonItem setTarget:yourTarget]
[self.navigationItem.backBarButtonItem setSelector:#selector(popToRootViewController:)]
Be aware that the backBarButtonItem should be set on the view controller in the stack that comes before the view controller where you would like the button to be visible:
self.navigationController.viewControllers
| ...
|
|-- SecondViewController <-- Set the backBarButtonItem here to be visible on LastViewController
|-- LastViewController
As far as I know, you can not pop to Root ViewController through the default back button because this default button is only for popping into the previous view Controller. So, if you want to pop back to the root ViewController, you can make make a custom bar button on the navigation bar and you can use popToRootViewControllerAnimated: in the selector method of this custom bar button. If you want to use only one back button for popping to root ViewController, you can simply hide your default back button by using this:
self.navigationItem.hidesBackButton = YES;
And then, you can add your custom back button on your navigation bar through which you can pop to root ViewController.
Please let me know if it works. Thanks :)
You can use viewWillDisappear
-(void) viewWillDisappear {
NSArray *viewControllers = self.navigationController.viewControllers;
if ([viewControllers indexOfObject:self] == NSNotFound) {
// View is disappearing because it was popped from the stack
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
Got some code from here: viewWillDisappear: Determine whether view controller is being popped or is showing a sub-view controller
This code will pop to the root view controller when the view is being popped (which happens when the back button is pressed).
You should subclass your UINavigationController and overwrite the method popViewControllerAnimated(animated: Bool).
override func popViewControllerAnimated(animated: Bool) -> UIViewController? {
guard let last = self.viewControllers.last else {
return nil
}
if last.isKindOfClass(MySecondLastViewController.self) {
return super.popToRootViewControllerAnimated(animated)![0]
} else {
if self.viewControllers.count >= 2 {
return super.popViewControllerAnimated(animated)
} else {
return nil
}
}
}
Related
Here is the problem
1) Rootview controller - MYAssetVC-> Embedded with NavigationController here pushing for button to another Addfilevc.
2) Addfilevc Has dropdownTextfield It will push to another vc have tableview selected row will display in the textfield.
3)if i select another value from the dropdown textfield it will push to the vc again there i will select.
Navigation bar back button will navigate to all my view hierarchy i want to handle this one. if i go to same view it should navigate back only once that to the recent visit how to do this.
As i am new to iOS. give any suggestion.
Navigation from 1->2->3
navigation backbtn 3->2->1
if i navigate like this 1->2->3-> backbutton 3->2 again 2->3 backbutton 3->2 again 2->3
IF i navigate now using back it is displaying all my route path it should navigate like 1->2->3> and 3->2->1 if any number of times i perform actions in 2 & 3.
1,2,3 are view controllers.
Create an IBAction for the back button and use popViewController.
[self.navigationController popViewControllerAnimated:YES];
This will help you to go back one page. You have to write this in all the pages where there is a back button and you want to go back one page.
If you want to go back directly to rootViewController, try this:
[self.navigationController popToRootViewControllerAnimated:YES];
And if you want to pop to any specific viewController in the stack, you run a for loop to find the viewController you want to navigate to and then simply popToViewController, like this:
for (UIViewController *viewController in self.navigationController.viewControllers) {
if ([viewController isKindOfClass:[Addfilevc class]]) {
[self.navigationController popToViewController:viewController animated:YES];
}
}
Hope this helps to clear your concept.
EDIT
In swift:
The [self.navigationController popViewControllerAnimated:YES]; will become self.navigationController?.popViewControllerAnimated(true)
The [self.navigationController popToRootViewControllerAnimated:YES]; will become navigationController?.popToRootViewControllerAnimated(true)
And the for loop will be as below:
You can use this if you are using storyboard
let switchViewController = self.storyboard?.instantiateViewControllerWithIdentifier("view2") as ComposeViewController
self.navigationController?.pushViewController(switchViewController, animated: true)
And the for-in loop
if let viewControllers = self.navigationController?.viewControllers {
for viewController in viewControllers {
self.navigationController!.popToViewController(viewController, animated: true);
}
}
Thanks.
I have storyboard of several view controllers embedded in Navigation Controller.
Due to navigation logic in later views of storyboard, the back button (in the left upper corner, in navigation bar) does not go back to the first view. I am wondering where and how to change this behaviour of Back button of second view only. Appreciate any ideas, examples.
Of course you could implement a custom back button. But there is also a nice way to keep using the default button.
Simply check if the current viewController is still in the navigation stack in viewWillDisappear before you call super.viewWillDisappear(). If it is not, the back button has been pressed. Then you can do the popToRootViewControllerAnimated.
override func viewWillDisappear(animated: Bool) {
if (navigationController?.viewControllers)!.contains(self) {
// back button was pressed
self.navigationController?.popToRootViewControllerAnimated(animated)
}
super.viewWillDisappear(animated)
}
The custom back button appears to be the best solution. The code inside your action method would look like the following in swift:
self.navigationController?.popToRootViewControllerAnimated(true)
//Just change the true to false if you don't want it animated.
I hope this helps (if you had not already found the answer). Cheers!
If you meant to say: Prevent Back Button Navigating to Previous Controller and move to first view controller:
You could do this by creating a custom back button - drag a button in storyboard to the top left of the navigation bar, and wire it up to your view controller. In your custom back button's selector write:
[self.navigationController popToRootViewControllerAnimated:YES]
Assuming you have a view controllers stack like : VC1 > VC2 > VC3, and you want to back to VC1 when tapping back on VC3, then you could set this code in VC2 :
[self.navigationItem setBackBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:#"Back" style:(UIBarButtonItemStylePlain) target:self action:#selector(backToVC1)]];
Then, always in VC2 :
- (void)backToVC1
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
I have a tabbar controller. In one of the viewcontroller children I do a check and determine if I will show a login view with this code:
if(loggedIn){
}else{
SignupViewController *svc = [self.storyboard instantiateViewControllerWithIdentifier:#"SignupView"];
svc.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:svc animated:YES];
}
From here the user goes through several subsequent views in wizard format. Like from SignupViewController1->SignupViewController2->SignupViewController3->etc. As #Rocky pointed out, you cannot pop SignupViewController1 off the stack while you are in SignupViewController2 or 3 or any subsequent viewController.
I know the iOS docs state the following:
If YES, the bottom bar remains hidden until the view controller is
popped from the stack.
My question is how do I get acccess to the original SignupViewController to pop it off the stack to see my tabbar again once I have moved to subsequent views in its navigation controller?
(The next answer is for SWIFT, but, you can traduce it to Objective-C)
I do not know, but maybe you can try that:
#IBAction func returnToPreviusScene(sender : AnyObject) {
let navController:UINavigationController = self.navigationController!
navController.popViewControllerAnimated(true)
navController.pushViewController(FatherView.singleton, animated: false)
}
You need to know:
singleton is a static variable from the VIEW FatherView
I have a tab bar controller with 4 views controller, and have this tab bar controller in a navigation controller.
I want to display a UIBarButtonItem for just one specific view controller of the tab bar controller.
I tried to use the following
if (tabBarController.selectedViewController == customTourViewController)
{
[tabBarController.navigationItem setRightBarButtonItem:done];
}
But the button doesn't show up.
If I put every view controller in a navigation controller, then the button shows up for only that view, but I end up having 2 navigation bars.
Is there any way I can implement the first solution?
Thanks.
In my individual view controllers for the individual tabs, I have the following in the one that needs the button:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonSystemItemDone target:nil action:nil];
self.tabBarController.navigationItem.rightBarButtonItem = rightButton;
}
And in the view controllers that don't need the button, I have:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.tabBarController.navigationItem.rightBarButtonItem = nil;
}
So, if it's not working for you, I'm not sure if it's your reference to tabBarController without the self designation (if I omit the self I get a compiler error). And where is this code because if it's in your tabBarController subclass, then you want self.navigationItem.rightBarButtonItem, right? Do you have your own ivar defined for that variable name? Or are you sure that done is defined properly (i.e. not nil)? Or are you sure this code is being called at all (perhaps set a breakpoint or insert a NSLog and make sure this code is being reached)?
Alternatively you can implement viewWillDisappear in the same view where you need the button.
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
self.tabBarController.navigationItem.rightBarButtonItem = nil;
}
The accepted answer above is exactly what I needed, just wanted to convert it to Swift for those in the future.
I've added the code below to the view controller that required the bar button (I've created an add button for this example):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: nil)
}
In the view controllers that don't require this bar button, simply add the code below
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.rightBarButtonItem = nil
}
You use viewWillAppear vice viewDidAppear because you want the bar button to appear every time the user goes to the designated view controller.
Simply put, viewDidAppear is called once at runtime, viewWillAppear will be called every time the view controller is visited.
I am trying to do the following.
I have a tab bar controller with 2 tabs in it. Both the tabs are navigation controller with a table view on each of them.
Now when I select one cell of the table in the first tab, I am pushing another tab bar controller, so I would like to hide the tab bar of the parent tabbarcontroller, and when I click the back button on the navigation bar I would like to see the the parent tab bar again, as I am in my parent tab bar view.
I tried hidesbottombarwhenpushed and it hides the parent tab bar controller tab bar but when I click back it doesn't brings it back.
Ok, So finally I got my answer, this is what I am suppose to do.
self.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:aViewController animated:YES];
self.hidesBottomBarWhenPushed=NO;
So basically hidesBottomBarWhenPushed = YES, and then push your view controller and then hidesBottomBarWhenPushed = NO; this works like a charm.
Thanks to eddy and his post here
The accepted answer had a problem to me.
My app had a navigation with the depth of three UIViewController.
The FirsViewController show's the UITabBar. (Correct)
The FirsViewController pushes the SecondViewController, and the SecondViewController don't show's the UITabBar. (Correct)
The SecondViewController pushed the ThirdViewController, and the ThirdViewController show's the UITabBar. (Incorrect)
The ThirdViewController popped to the SecondViewController, and the SecondViewController show's the UITabBar. (Incorrect)
The SecondViewController popped to the FirstViewController, and the FirstViewController show's the UITabBar. (Correct)
The solution for me was setting the delegate of UINavigationControllerDelegate
swift:
self.navigationController?.delegate = self
Objective-c:
self.navigationController.delegate = self;
And then implement the following delegate method
Swift:
fun navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if fromVC.isKindOfClass(FirstViewController) && toVC.isKindOfClass(SecondViewController) {
self.hidesBottomBarWhenPushed = true;
}
else if fromVC.isKindOfClass(SecondViewController) && toVC.isKindOfClass(FirstViewController) {
self.hidesBottomBarWhenPushed = false;
}
return nil
}
Objective-c:
-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController*)fromVC
toViewController:(UIViewController*)toVC
{
if ([fromVC isKindOfClass:[FirstViewController class]] && [fromVC isKindOfClass:[SecondViewController class]]) {
self.hidesBottomBarWhenPushed = true;
}
else if ([fromVC isKindOfClass:[SecondViewController class]] && [fromVC isKindOfClass:[FirstViewController class]]) {
self.hidesBottomBarWhenPushed = false;
}
return nil;
}
Hope it helped.
As the Apple documentation states, you can't push a UITabBarController instance on a NavigationController. And there's a good reason for that: how do you get back from the pushed tab bar controller if you selected another item in the tab bar?
The simple answer is: don't do that, it will confuse your user. You can try swapping the first view controller for another view controller that may be a tab bar controller, but do not use the push paradigm for that: use an explicit button instead that will swap your first tab bar controller for the second one, preferably using a visual transition.
You can look at the setAnimationTransition:forView:cache: documentation for the UIView class to know how to swap, say, a tab bar controller for another:
Begin an animation block.
Set the transition on the container view.
Remove the subview from the container view.
Add the new subview to the container view.
Commit the animation block.
In this case, the container view will be the application's window.
You can also hide it using the attributes inspector when select the tabBar controller
Set hidesBottomBarWhenPushed = true in the controller that will be pushed.
For hide all controllers put into prepare for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
segue.destination.hidesBottomBarWhenPushed = true
}
In your FirstViewController use
self.hidesBottomBarWhenPushed = true
in your SecondViewController use
override func willMoveToParentViewController(parent: UIViewController?) {
if parent == nil {
var viewControllers = self.navigationController!.viewControllers
if ((viewControllers[viewControllers.count - 2]).isKindOfClass(FirstViewController.self)) {
(viewControllers[viewControllers.count - 2] as! FirstViewController).hidesBottomBarWhenPushed = false
}
}
}