99

I'm using following code to programmatically navigate to another ViewController. It works fine, but it some how hides the navigation bar. How do I fix this? (the navigation bar is created by embeding the ViewController in the navigation controller if that matters.)

let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)

let nextViewController = storyBoard.instantiateViewControllerWithIdentifier("nextView") as NextViewController
self.presentViewController(nextViewController, animated:true, completion:nil)
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Victor
  • 1,603
  • 4
  • 17
  • 24

8 Answers8

239

Swift 5

The default modal presentation style is a card. This shows the previous view controller at the top and allows the user to swipe away the presented view controller.

To retain the old style you need to modify the view controller you will be presenting like this:

newViewController.modalPresentationStyle = .fullScreen

This is the same for both programmatically created and storyboard created controllers.

Swift 3

With a programmatically created Controller

If you want to navigate to Controller created Programmatically, then do this:

let newViewController = NewViewController()
self.navigationController?.pushViewController(newViewController, animated: true)

With a StoryBoard created Controller

If you want to navigate to Controller on StoryBoard with Identifier "newViewController", then do this:

let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "newViewController") as! NewViewController
        self.present(newViewController, animated: true, completion: nil)
AlphaWulf
  • 354
  • 5
  • 11
jaiswal Rajan
  • 4,641
  • 1
  • 27
  • 16
  • 2
    "as! NewViewController" is not needed in the storyboard option – Lavi Avigdor Jun 19 '17 at 09:38
  • 2
    yeh i know that is optional, but if we show then it becomes clear which viewcontroller is destination for another developer – jaiswal Rajan Jun 19 '17 at 10:21
  • 5
    Call `storyBoard.instantiateViewController` and `self.present` from the main thread, or you will have a delay in appearing viewController components – Alex Jul 25 '18 at 11:23
  • 2
    Hi peeps: as a general rule, may I suggest a friendlier tone? (This is quite an issue here on SO) To the topic, instead: although it's not directly asked by OP, I think this good answer is still an appropriate place to remember (to anyone reading) that action has to happen on main thread – superjos Apr 03 '19 at 10:36
  • 1
    Just a note for me and anyone who has the same problem: It doesn't work if the code is put in the viewDidLoad function – bfhaha Oct 01 '19 at 19:30
  • Hi, How does one use self.present with Swift 5? It is showing an error now when I use self.present(newViewController, animated: true, completion: nil). Thanks for your help – user3064009 Jan 29 '21 at 05:28
42

SWIFT 4.x

The Strings in double quotes always confuse me, so I think answer to this question needs some graphical presentation to clear this out.

For a banking app, I have a LoginViewController and a BalanceViewController. Each have their respective screens.

The app starts and shows the Login screen. When login is successful, app opens the Balance screen.

Here is how it looks:

enter image description here

enter image description here

The login success is handled like this:

let storyBoard: UIStoryboard = UIStoryboard(name: "Balance", bundle: nil)
let balanceViewController = storyBoard.instantiateViewController(withIdentifier: "balance") as! BalanceViewController
self.present(balanceViewController, animated: true, completion: nil)

As you can see, the storyboard ID 'balance' in small letters is what goes in the second line of the code, and this is the ID which is defined in the storyboard settings, as in the attached screenshot.

The term 'Balance' with capital 'B' is the name of the storyboard file, which is used in the first line of the code.

We know that using hard coded Strings in code is a very bad practice, but somehow in iOS development it has become a common practice, and Xcode doesn't even warn about them.

zeeshan
  • 4,913
  • 1
  • 49
  • 58
21

You should push the new viewcontroller by using current navigation controller, not present.

self.navigationController.pushViewController(nextViewController, animated: true)
Okan Kocyigit
  • 13,203
  • 18
  • 70
  • 129
  • why should we push is there any benefit? – DragonFire Jul 11 '19 at 09:25
  • 1
    @DragonFire, because op wants it works without covering the navigation bar. if you want to have a master-detail page design like between whatsapp contacts screen and chat screen you must `push` the viewcontroller with your navigationcontroller. (that will animate left to right) if you just want to show, present, pop-up on the current screen (that will animate from bottom to top), just use `present`. – Okan Kocyigit Jul 12 '19 at 14:12
  • Thank you! Works a treat keeping my navigation controller tabs etc. – David_2877 Dec 17 '20 at 14:44
14

According to @jaiswal Rajan in his answer. You can do a pushViewController like this:

let storyBoard: UIStoryboard = UIStoryboard(name: "NewBotStoryboard", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "NewViewController") as! NewViewController
self.navigationController?.pushViewController(newViewController, animated: true)
Andres Paladines
  • 1,142
  • 14
  • 19
6

So If you present a view controller it will not show in navigation controller. It will just take complete screen. For this case you have to create another navigation controller and add your nextViewController as root for this and present this new navigationController.

Another way is to just push the view controller.

self.presentViewController(nextViewController, animated:true, completion:nil)

For more info check Apple documentation:- https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/doc/uid/TP40006926-CH3-SW96

Aks
  • 1,567
  • 13
  • 23
5
OperationQueue.main.addOperation {
   let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
   let newViewController = storyBoard.instantiateViewController(withIdentifier: "Storyboard ID") as! NewViewController
   self.present(newViewController, animated: true, completion: nil)
}

It worked for me when I put the code inside of the OperationQueue.main.addOperation, that will execute in the main thread for me.

badhanganesh
  • 3,427
  • 3
  • 18
  • 39
Pedro Berbel
  • 71
  • 1
  • 2
1

All other answers sounds good, I would like to cover my case, where I had to make an animated LaunchScreen, then after 3 to 4 seconds of animation the next task was to move to Home screen. I tried segues, but that created problem for destination view. So at the end I accessed AppDelegates's Window property and I assigned a new NavigationController screen to it,

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let homeVC = storyboard.instantiateViewController(withIdentifier: "HomePageViewController") as! HomePageViewController

//Below's navigationController is useful if u want NavigationController in the destination View
let navigationController = UINavigationController(rootViewController: homeVC)
appDelegate.window!.rootViewController = navigationController

If incase, u don't want navigationController in the destination view then just assign as,

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let homeVC = storyboard.instantiateViewController(withIdentifier: "HomePageViewController") as! HomePageViewController
appDelegate.window!.rootViewController = homeVC
Deitsch
  • 1,610
  • 14
  • 28
shubham mishra
  • 1,183
  • 14
  • 21
1

The above code works well but if you want to navigate from an NSObject class, where you can not use self.present:

let storyBoard = UIStoryboard(name:"Main", bundle: nil)
if let conVC = storyBoard.instantiateViewController(withIdentifier: "SoundViewController") as? SoundViewController,
    let navController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
    
    navController.pushViewController(conVC, animated: true)
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Bhargav
  • 71
  • 3