How to press "Back" button in UINavigationController programmatically - iphone

I have a UIViewController called FriendsViewController inside a UINavigationController. And a second UIViewController called FriendsDetailedViewController. When navigating from the first view controller to the second, I want to programmatically press the Back button when needed. How to do this?

Simply use
[self.navigationController popViewControllerAnimated:YES]
from FriendsDetailedViewController. Your view will be popped out i.e. the behavior of back button.
Note that it returns UIViewController on normally, and returns nil if there is nothing to pop.

If by pressing "Back" button you mean just to go to the previous view controller, you can just call:
[self.navigationController popViewControllerAnimated:YES];

Here is the swift method
if let navController = self.navigationController {
navController.popViewControllerAnimated(true)
}

Swift 5
self.navigationController?.popViewController(animated: true)
Usage in a code snippet:
func unwindViewController() {
self.navigationController?.popViewController(animated: true)
}

1) When you pop in current NavigationController Then
In Swift
self.navigationController?.popViewControllerAnimated(true)
Objective C
[self.navigationController popViewControllerAnimated:YES];
2) When you back another Navigation controller Then
In Swift
let story = UIStoryboard(name: "Main", bundle: nil)
let pushVC = story.instantiateViewControllerWithIdentifier("PushVC")
let navigation = story.instantiateViewControllerWithIdentifier("homeNavigation") as! UINavigationController
navigation.pushViewController(pushVC!, animated: true)
In Objective C
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"storyBoardName" bundle:nil];
pushVC* ObjectOfPushVC = [storyboard instantiateViewControllerWithIdentifier:#"pushVC"];
[self.navigationController pushViewController:ObjectOfPushVC animated:YES];

Here is how I did it in Swift 3
_ = self.navigationController?.popViewController(animated: true)
_ is used to suppress the ugly warning generated by XCode.

Related

How to do popViewController from another class?

My app has a TabBarController. Each tabBarItem relates to a ViewController embedded in a NavigationController.
When in first tabBarItem and selecting another tabBarItem I want to do some stuff before moving to the selected ViewController. Therefore I created a class for my tabBarController and made it UITabBarControllerDelegate.
What I want to do is present an alert with two buttons; button A cancels the move to the selected viewController and button B lets the move happen.
My problem is that when button B is pressed, I want to popToRootViewController. I gave the navigationController a storyboardID and tried to instantiate it like shown below.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let alert = self.storyboard?.instantiateViewController(withIdentifier: "ActiveSessionWarningAlert") as? ActiveSessionWarningAlert {
alert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
alert.modalTransitionStyle = UIModalTransitionStyle.flipHorizontal
let alertWasDismissed: (Bool) -> Void = { userWantsToMoveToSelectedViewController in
if userWantsToMoveToSelectedViewController {
if let navContr = self.storyboard?.instantiateViewController(withIdentifier: "firstNavContr") as? UINavigationController {
navContr.popToRootViewController(animated: true)
}
tabBarController.selectedViewController = viewController
}
}
alert.alertWasDismissed = alertWasDismissed
self.present(alert, animated: true, completion: nil)
}
return false
}
Everything works as expected, but the popToRootViewController doesn't seem to occur; when selecting the first tabBarItem again the same viewController that was 'active' when we left the item is still showing.
I checked so that the viewController I want to pop is actually in the navigation stack and that the navContr != nil.
What am I missing?
You don't say so, but I'm assuming that the alertWasDismissed closure you pass to your alert view controller gets invoked when the user dismisses the alert.
The problem with your closure is this bit:
if let navContr = self.storyboard?.instantiateViewController(withIdentifier: "firstNavContr") as? UINavigationController
Any time you call instantiateViewController(withIdentifier:), you are creating a brand new, never before seen instance of a view controller (a navigation controller in this case.) That navigation controller has nothing to do with the one that belongs to the current tab that you are trying to dismiss. It doesn't have anything in it's navigation stack other than the root view controller that is defined in the storyboard.
What you need to do is find the navigation controller of the current tab in your tab bar controller's tabBarController(_:shouldSelect:) method, and pass that navigation controller to your alertWasDismissed closure.
At the time the tabBarController(_:shouldSelect:) method is called, the tab bar controller's selectedViewController should contain the current view controller. Replace the line above with:
if let navContr = tabBarController.selectedViewController? as? UINavigationController {}
That should work.

Present viewcontroller with tabbar

I created UITabBarController programmatically in AppDelegate with 4 view controllers(using .xib). When user tap some button on ViewController (VC-A) it present another VC (VC-B) and covered tabbar. So I want to VC-B has a tabbar on the button.
I tried to add VC-B as a child of tabbarcontroller. I tried to .present(vc) and .show(vc) on both: VC-A and VC-A.TabBarController
Creating controllers in AppDelegate:
let controllers = [tabViewController1,tabViewController2,tabViewController3,tabViewController4]
tabBarController.viewControllers = controllers
window?.rootViewController = tabBarController
presenting in VC-A
self.tabBarController?.present(controller, animated: false, completion: nil)
right click and drag from tabbar controller in storyboard to VC-B. that should create a tab on the bottom of your VC-A and VC-B to go back and forth without having to implement any backend code unless you want to animate
The solution is to embed every VC in navigationController and then add to TabBarController.
let vc1 = ViewController1()
let navController1 = UINavigationController(rootViewController: vc1)
navController.isNavigationBarHidden = true
let controllers = [navController1, navController2, navController3, navController4]
tabBarController.viewControllers = controllers
window?.rootViewController = tabBarController
Then call
self.navigationController?.pushViewController(controller, animated:
true)
To diplay VC with tabbar
I will press the red login button at the bottom of the picture and try to log in.
login button => "로그인"
After that, log in.
let moreVC = self.storyboard?.instantiateViewController(withIdentifier: "MoreViewController") as! MoreViewController
moreVC.definesPresentationContext = true
moreVC.modalPresentationStyle = .fullScreen
let navController = UINavigationController(rootViewController: moreVC)
self.present(navController, animated: true, completion: nil)
If the login proceeds without error, the above code will be called to display the screen when the login is completed.
If the flow proceeds as the code above, this screen appears.
The screen shown is not fullscreen, and the tabbar at the bottom is gone. The screen I want is the screen below.
How can I present a tab bar when presenting the screen?

Xcode 8, adding UIButtons to NavigationBar for non initial view controllers

I'm trying to achieve what I think would be a simple task, but despite similar posts on here being answered, the solution eludes me....
I'm using Main.storyboard in Xcode 8/swift 3 to create an application with the initial ViewController being a UINavigationController. I then want to push to UITabBarController which has two view controllers which it holds a relationship with:
override func viewDidLoad() {
super.viewDidLoad()
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let vc = mainStoryboard.instantiateViewController(withIdentifier: "test") as? UITabBarController {
self.navigationController?.pushViewController(vc, animated: true)
}
}
When launching the app, the initial ViewController successfully 'pushes' to the TabBarController/it's ViewControllers (image below). The issue I'm having is after adding navigation items/buttons in the TabBarController ViewControllers (either in Storyboard or programmatically) the buttons / nav items never show.
Storyboard setup
Simulator screenshot
I have seen a few posts such as the below links and have followed the suggested steps verbatim, but nothing has solved the problem. Any help would be greatly appreciated!
Adding buttons to navigation controllers
How to add buttons to navigation controller visible after segueing?
It's does not show, because your TabBarController also have his own UINavigationBar. ViewControllers are inside TabBarController
you can create custom TabBarController and handle tabs actions
Try this code:
class TabBarController: UITabBarController {
override var selectedViewController: UIViewController? {
didSet {
switch self.selectedViewController {
case self.selectedViewController is FirstViewController:
self.navigationItem.rightBarButtonItem = self.firstButton
case self.selectedViewController is SecondViewControlller:
self.navigationItem.rightBarButtonItem = self.secondButton
default:
break
}
}
}
}

hide tab bar in view with push

I have a tabBar + NavigationViewController. The Tab bar has collection view with cells(Say view1) and with cells a push seague is implemented to another view(Say view2).
In view2 I want to have a navBar but no tab bar.
I tried
self.tabBarController?.tabBar.hidden = true,
it worked fine for view2 but when I went back to view1 by back button the tab was still hidden( even after in view1 class I added self.tabBarController?.tabBar.hidden = false in viewDidLoad func).
How can i make the tab bar reappear in view1?
I'm working in swift.
Make sure to check this option only on the ViewController whose tab
bar you wish to be hidden.
Thanks to iHarshil for the suggestion.
In the viewDidload set the UIViewController hidesBottomBarWhenPushed to yes:
self.hidesBottomBarWhenPushed = YES;
This way the UINavigationController takes care of hiding the tab bar.
Use in prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showRecipeDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeDetailViewController *destViewController = segue.destinationViewController;
destViewController.recipeName = [recipes objectAtIndex:indexPath.row];
// Hide bottom tab bar in the detail view
destViewController.hidesBottomBarWhenPushed = YES;
}
}
Bruno Fernandes's answer in Swift:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "YourSegueIdentifier" {
let destinationController = segue.destinationViewController as! YourViewController
destinationController.hidesBottomBarWhenPushed = true
}
}
This was the answer that worked for me. Putting hidesBottomBarWhenPushed in the viewDidLoad method didn't work.
Thanks Bruno!
In my case, I use hidesBottomBarWhenPushed before I push the destination view controller.
func showSecondViewController() {
let vc = SecondViewController()
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)
}
You have to work with viewWillAppear or viewDidAppear. viewDidLoad will be called when view1 is loading (showing) the first time. If you move from view1 to view2 and back the viewDidLoad won't be called again. Therefore you have to use viewWillAppear or viewDidAppear like
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.hidden = false
}
Put this code in your view1 controller. The viewWillAppear or viewDidAppear will be called every time navigating back to view1
if you want to hide TabBarController Bottom Bar : #Swift 3
In YourViewController : in ViewDidLoad() method
self.tabBarController?.tabBar.isHidden = false

Adding a NavigationBar to UITableViewController programmatically?

I am trying to create a uitableviewcontroller as a modal viewcontroller to edit some settings. I am creating the tableviewcontroller in code and the thing i am struggling with currently is how to correctly add a navigation bar to the controller which will have a "Done" button on it that:
a) doesnt appear on top of the tableview and
b) does not scroll with the tableview??
This happens when i add the navbar to the contoller with:
[self.view addSubview:navigationBar];
This adds a navbar to the controller which goes on top and obscures the tables first row and also scrolls with the view?
I also thought about simply using a uiviewcontroller with a separate tableview however i like the funcitonality of automatically scrolling the tableview when editing a textfield that the tableviewcontroller gives you. Just cant figure how to setup this navbar??
thx
Just create UINavigationcontroller as the modal viewcontroller, and add the tableview as its root view controller.
Use Navigation controller as modalviewController(as suggested in the other answer). Here is the code:
UINavigationController *Controller = [[UINavigationController alloc] init];
//LoginViewController is a sub class of UITableviewController
LoginViewController *controller = [[LoginViewController alloc] init];
Controller.viewControllers=[NSArray arrayWithObject:controller];
[self presentModalViewController:Controller animated:YES];
[controller release];
[Controller release];
In Swift:
AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
/*
#1. Instantiate a navigation controller and add the table view controller
as a child view controller.
*/
let navigationController = UINavigationController()
// Instantiate your desired ViewController
let storyboard = UIStoryboard(name: UIStoryboardName.Main.rawValue, bundle: nil)
let tableViewController = storyboard.instantiateViewControllerWithIdentifier("TableViewControllerID")
navigationController.addChildViewController(tableViewController)
/*
#2. Then we set the title of the navigation bar and add two bar button items.
*/
// We set the title of the navigation bar.
tableViewController.navigationItem.title = "My Title"
// Create left and right button for navigation item.
let leftButton = UIBarButtonItem(title: "Save", style: UIBarButtonItemStyle.Plain, target: tableViewController, action: "saveButtonClicked:")
let rightButton = UIBarButtonItem(title: "Right", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
// Create two buttons for the navigation item.
tableViewController.navigationItem.leftBarButtonItem = leftButton
tableViewController.navigationItem.rightBarButtonItem = rightButton
/*
#3. To finish we set the root view controller with the navigation controller.
*/
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
return true
}
TableViewController.swift
// Method called when the user clicks on the Save bar button item.
func saveButtonClicked(sender: UIBarButtonItem) {
// Do something.
print("Save bar button item has been clicked!!!")
}