Setting back button when presenting a view controller - ios

On clicking a button I display a view controller using the following code:
let navController = UINavigationController(rootViewController: locationVC)
navController.navigationBar.barTintColor = StyleHelper.navBarColor()
navController.navigationBar.tintColor = UIColor.whiteColor()
navController.navigationItem.backBarButtonItem = UIBarButtonItem(image: UIImage(named: "back_arrow"), style: .Plain, target: locationVC, action: nil)
self.presentViewController(navController, animated: true, completion: nil)
But the back button does not appear on the locationVC
What am I doing wrong? Please help

Multiple issues with code.
Issue 1:
You should update viewController's bar button item and not navigationController's bar button item.
So this is wrong
navController.navigationItem.backBarButtonItem
Whats correct
self.navigationItem.backBarButtonItem
Issue 2:
The above code won't work because your current viewController is not pushed by any other VC so it cant show back button. What you need is leftBarButtonItem
So in your VC you can write
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Abcd", style: .done, target: self, action: yourSelectorHere)
}
O/P:

Back button only appears for view controllers in the navigation stack.
you creating new navigation controller stack and making locationVC as root controller so there won't be any view controller to go back.
If you push another view controller to the navigation stack then you will get back button.

In a horizontally regular environment, the view controller is
presented in the style specified by the modalPresentationStyle
property. In a horizontally compact environment, the view controller
is presented full screen by default. doc: presentViewController
So, if you use present a view controller it will not show in navigation controller thus no back button
For this case you need to push view controller.
From storyboard:
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewController(withIdentifier:
"newViewController") as! YourViewController
self.navigationController?.pushViewController(vc, animated: true)
Programmatically
let vc = YourViewController()
self.navigationController?.pushViewController(vc, animated: true)

presenting will present your VC on top of everything and not push it to the navigayionCOntroller.
You should push your VC to the NavigationController instead like so:
var rootViewController = self.window!.rootViewController as UINavigationController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var profileViewController = mainStoryboard.instantiateViewControllerWithIdentifier("profile") as ProfileViewController
rootViewController.pushToViewController(profileViewController, animated: true)

presentViewController is a method that bring a modal view to your navigation, not part of the navigationController.
Instead you should use the pushToViewController method on the navigationController (and not creating a new one) like that :
if let navigationController = self.navigationController {
navigationController.pushViewController(locationVC, animated: true)
}

The presenting view controller is responsible for dismissing the view controller is presented. If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal.
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
If you want to retain a reference to the view controller's presented view controller, get the value in the presentedViewController property before calling this method.
#IBAction func backButtonAction(_ sender: UIButton) {
self.dismiss(animated: true)
}
More: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss

Related

How To Remove The First ViewController In The Background?

I have two view controllers, first view controller and the second view controller, when the user segue from the first view controller to the second view controller, the first view controller still visible and sits behind the second view controller. The user can swipe the first view controller and see the entire first view controller and then they got stuck there. I noticed that this only happens after the launch of iOS 13.
performSegue(withIdentifier: "showSecond", sender: self)
Follow these two steps:
Click on the segue you created on the Storyboard
Go to Attributes Inspector
Set the Presentation to "Full Screen"
#Maysam is 100% right! But you can also do it programmatically through this:
// Go to next View Controller
#IBAction func nextTextVC(_ sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nextInfo = storyboard.instantiateViewController(identifier: "socialOptionsIntroViewController")
self.navigationController?.pushViewController(nextInfo, animated: true)
}
There is a more detail thread could answer this question.
The follow solution from Pratik Sodha
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
navigationController.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(navigationController, animated: true, completion: nil)

Tabbar not showing when i open from side menu

I am using this code
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "PrintMainViewController")
self.navigationController!.pushViewController(controller, animated: true)
and also added:
self.tabBarController?.tabBar.isHidden = false
If I understand your hierarchy correctly you have a tab bar controller which has navigation controllers in it. So basically any of the tabs can push additional view controllers and tab bar is still visible.
Now you want to push some new controller on the currently selected view controller in the tab bar and you want to do it from another part of the app, another view controller that has no relation to tab bar.
The quickest way to do that is to expose a static instance of your tab bar view controller. This will only work if you always have only 1 tab bar controller in your application (probably 99% of the applications).
First add a current instance to your tab bar view controller:
class MyTabBarViewController: UITabBarController {
static private(set) var currentInstance: MyTabBarViewController?
override func viewDidLoad() {
super.viewDidLoad()
MyTabBarViewController.currentInstance = self
}
}
So when view loads a static value is assigned and can now be accessed anywhere in your project via MyTabBarViewController.currentInstance.
The rest is then just accessing the currently selected view controller and pushing a new view controller. Something like this should do:
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "PrintMainViewController")
(MyTabBarViewController.currentInstance?.selectedViewController as? UINavigationController)?.pushViewController(controller, animated: true)
You must push in TabBarController
self.tabBarController?.pushViewController(controller, animated: true)

Navigation Bar Not Showing after Adding NavigationController

I have an existing UITableViewController that I've embedded in a NavigationController. However, the Navigation Bar is not showing when I present the view.
Presenting the TableViewController (its Storyboard id is: SelectServicesController) :
if let selectServicesController = self.storyboard?.instantiateViewControllerWithIdentifier("SelectServicesController") as? UITableViewController {
self.navigationController?.presentViewController(selectServicesController, animated: true, completion: nil)
}
This is what it looks like when I build (nav bar does not show):
So I just did this and at fist could not get it to show up at all. Then Figured it out, You just need to select the navigation controller and set it to be the ✅is initial View Controller
This is what your storyboard should look like
Then to make everything show up I added this to my viewDidLoad of the view the Navigation controller is presenting. This step is more optional.
self.navigationController?.navigationBar.barTintColor = UIColor.redColor()
self.navigationController?.navigationBar.tintColor = UIColor.blackColor()
self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.blackColor()]
navigationController?.navigationBar.hidden = false
And this is what it looks like
mmmm Red on black 🤓 Hope that helps you.
You're presenting a UITableViewController, which doesn't have a navigation controller as a parent (even though your Storyboard has it first, you're not actually using it).
You can fix this by doing something like this:
if let selectServicesController = self.storyboard?.instantiateViewControllerWithIdentifier("SelectServicesController") as? UITableViewController {
let navigationController = UINavigationController(rootViewController: selectServicesController)
self.navigationController?.presentViewController(navigationController, animated: true, completion: nil)
}
Or by setting the navigation controller as the initial view controller of the storyboard and then calling it like this:
if let selectServicesController = self.storyboard?.instantiateInitialViewController() {
self.navigationController?.presentViewController(selectServicesController, animated: true, completion: nil)
}
I encountered the same problem. I solved it by Changing the segue to the navigation controller that embeds the View Controller I want to display.
Hopefully it would work for you.
Let me know if it is a bad practice.
let storyboard = UIStoryboard(name: "Expense", bundle: Bundle(for: PTCAddExpenseViewController.self))
let controller = storyboard.instantiateViewController(withIdentifier:"AddExpense") as! PTCAddExpenseViewController
let navigationController = UINavigationController(rootViewController: controller)
self.present(navigationController, animated: true, completion: nil)
Adding this works for me:
self.navigationController?.isNavigationBarHidden = false
You're presenting the table view controller directly, not its navigation controller. If you mark the nav controller as the initial view controller (tick the "Is initial view controller" box in the attributes inspector), then you can instantiate and show it by:
if let selectServicesNavController = self.storyboard?.instantiateInitialViewController() as? UINavigationController {
// if you're pushing it onto an existing nav controller
self.navigationController?.presentViewController(selectServicesNavController, animated: true, completion: nil)
// if not (and this is probably the case), set the nav controller as your window's rootViewController
UIApplication.sharedApplication().keyWindow.rootViewController = selectServicesNavController
}
My guess is Xcode is ignoring the fact that your table view controller is embedded in navigation controller when presenting your table view controller with the following code:
if let selectServicesController = self.storyboard?.instantiateViewControllerWithIdentifier("SelectServicesController") as? UITableViewController {
self.navigationController?.presentViewController(selectServicesController, animated: true, completion: nil)
}
Instead, I would suggest you modify the Top Bar setting under Simulated Metrics to suit your needs or instantiate your navigation controller instead (the latter is preferred and recommended)
In your code, change this line
self.navigationController?.presentViewController(selectServicesController, animated: true, completion: nil)
to this
self.presentViewController(selectServicesController, animated: true, completion: nil)
I read in one of your comments you want to present the table view controller modally, with the navigation bar showing. We can do this using the Storyboard. From the view controller that should display this table view controller modally, Ctrl+Drag from the view controller to the Navigation Controller of the Table View Controller. Then, select Present Modally. Set an Identifier for the segue. Then, in your code for the view controller that is presenting the table view controller modally, call:
self.performSegueWithIdentifier("YourSegueIdentifier", sender: nil)
Another way to do this without having to use any code is if the modal presentation is being triggered by something like a button. Then, you can Ctrl+Drag from that button to the Navigation Controller and select Present Modally.
Or it might be this!!
I had the same problem: the navigation bar was showing on the root view in Storyboard, but when running the Simulator - there was no navigation bar at the top of the views. This solved it:
Navigation Controller > Navigation Bar > UNCHECK Translucent (it is checked by default). This did two things:
My Navigation Bar shows on all subsequent views.
The topmost subview is now at Y=0, and not Y=64.

Whose view is not in the window hierarchy after dismissViewController

I have a view controller that shows the details of an object. On this view controller is an "edit" button which shows modally the edition view controller. When I try to dismiss the modally presented view (edit view controller) :
self.dismissViewControllerAnimated(true, completion: nil)
I get the following error and it's presenting my initial viewController instead :
Warning: Attempt to present ≤Deevent.MyEventsVC: 0x7f99b70160a0≥ on ≤Deevent.EventCreationVC: 0x7f99b7238690≥ whose view is not in the window hierarchy!
So what I've tried was to set the root view controller of my view to the view I wanted to go back and present it in the completition of my dismiss. It's working well, but my application is in a Tabbar Controller and now it's not in it anymore. Same for the navigation controller.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("MyEventsStoryboard") as! MyEventsVC
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
appDelegate.window?.rootViewController = vc
self.dismissViewControllerAnimated(false, completion: {
self.presentViewController(vc, animated: true, completion: nil)
})
Is there an other approach for presenting viewControllers after dismiss without leaving the Tabbar controller ?
Thanks
Since you are trying to present the new view controller by calling self.presentViewController after you dismiss self, you get the error. I can offer you a solution if you are using a navigation controller.

Programmatically create navigation controller with back button

I haven't been able to figure something out in swift in my AppDelegate. I've been working on this little problem for weeks (in the background, not continuously) with lots of googling. I must be missing something.
When a remote notification comes in to my iOS app I'd like to modally present the View appropriate for the message, with a UINavigationController that has a Back button. The idea is that when the user is finished dealing with the notification, they can press "Back" and go back to where they were. I can't get a "Back" button to show up in my navigation controller programmatically.
Not all of my existing views are embedded in navigation controllers. So, rather than finding an existing UINavigationController in the stack, I've been creating a new UINavigationController and trying to set it up and present it like this:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var mvc = mainStoryboard.instantiateViewControllerWithIdentifier("MyViewIdentifier") as! MySpecialViewController
/* here is where I setup the necessary variables in the mvc view controller, using data from the remote message. You don't need to see all that */
/* this seems to work, I do get what looks to be a navigation controller */
let newNavController = UINavigationController(rootViewController: mvc)
/* this part doesn't work, I don't get a "Back" button */
let btn = UIBarButtonItem(title: "back", style: UIBarButtonItemStyle.Done, target: mvc, action: "backPressed")
newNavController.navigationItem.leftBarButtonItem = btn
/* this block seems to work. The idea is to work through the hierarchy of any modal presentations
until we get to the screen that is actually showing right now. I'm not
sure this will work in all situations, but it seems to work in my app's hierarchy. */
var currentViewController = self.window!.rootViewController
while currentViewController?.presentedViewController != nil {
currentViewController = currentViewController?.presentedViewController
}
/* this I suppose shows my new view controller modally, except without
the "back" button it's hard to be sure the navigation controller is even there. */
currentViewController?.presentViewController(newNavController, animated: true, completion: nil)
I do have a method called "backPressed" in the relevant view controller.
Any clues as to why the navigation bar button isn't showing up?
Edit: I knew that backBarButtonItem only shows up when we aren't at the top of the stack. But, leftBarButtonItem should show up even if we're at the top of the stack?
Here is simple example for you which can set navigation bar Programmatically from first View:
if let resultController = storyboard!.instantiateViewControllerWithIdentifier("SecondView") as? SecondView {
let navController = UINavigationController(rootViewController: resultController) // Creating a navigation controller with resultController at the root of the navigation stack.
self.presentViewController(navController, animated:true, completion: nil)
}
If you wan to add back button into that navigation then use this code into SecondView.swift class:
override func viewDidLoad() {
super.viewDidLoad()
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "goBack")
navigationItem.leftBarButtonItem = backButton
}
func goBack(){
dismissViewControllerAnimated(true, completion: nil)
}
If you want to do all of this from firstView then here is your code:
#IBAction func btnPressed(sender: AnyObject) {
if let resultController = storyboard!.instantiateViewControllerWithIdentifier("SecondView") as? SecondView {
resultController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "goBack")
let navController = UINavigationController(rootViewController: resultController) // Creating a navigation controller with VC1 at the root of the navigation stack.
self.presentViewController(navController, animated:true, completion: nil)
}
}
func goBack(){
dismissViewControllerAnimated(true, completion: nil)
}
Hope this will help you.
I take it that your view controller is used by more than one segue?
Within storyboard, you can embed the view controller in a navigation controller.
For the modal presentation, you segue to the navigation controller instead of the view controller. This will supply a bar for the modal presentation.
A Done button is more likely used to dismiss a modal presentation. (Users might be confused by that atypical use of a back button, as your presented navigation controller is not popping a view controller off its stack.)

Resources