I'm building an iOS app using a Storyboard. The root view controller is a Home View Controller. I'm creating the login/logout process, and it's mostly working fine, but I've got a few issues. I need to know the BEST way to set all this up.
When I tap on login and logout the app works fine but when I tap on Sign up Nothing works I have created a navigation controller too.
I'm just not sure what the best method is at this point.
I'm Giving you My Details
This Is MY Switcher Class For Managing View
import UIKit
class Switcher {
static func updateRootViewController() {
let status = UserDefaults.standard.bool(forKey: Constants.kUserDefaults.isSignIn)
var rootViewController : UIViewController?
#if DEBUG
print(status)
#endif
if (status == true) {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let mainTabBarController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.homeViewController) as! HomeViewController
rootViewController = mainTabBarController
} else {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let signInViewController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.signInViewController) as! SignInViewController
rootViewController = signInViewController
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = rootViewController
}
}
My App Delegate
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let storyBoard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
IQKeyboardManager.shared.enable = true
Switcher.updateRootViewController()
return true
}
Sign UP Button
#IBAction func signUpBtnTapped(_ sender: UIButton) {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
guard let signUpVC = storyBoard.instantiateViewController(withIdentifier: "RegistrationViewController") as? RegistrationViewController else {
return
}
print("Register Tapped")
self.navigationController?.pushViewController(signUpVC, animated: true)
}
The issue is the your rootViewController does not have a UINavigationViewController set up, for neither of the Viewcontrollers.
what you need to do is instead of instantiating like this:
if (status == true) {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let mainTabBarController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.homeViewController) as! HomeViewController
rootViewController = mainTabBarController
} else {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let signInViewController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.signInViewController) as! SignInViewController
rootViewController = signInViewController
}
You need to instantiate a UINavigationController which it's root is these like the following
if (status == true) {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let mainTabBarController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.homeViewController) as! HomeViewController
let navigationController = UINavigationController(rootViewController: mainTabBarController)
rootViewController = navigationController
} else {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let signInViewController = mainStoryBoard.instantiateViewController(withIdentifier: Constants.StoryboardID.signInViewController) as! SignInViewController
let navigationController = UINavigationController(rootViewController: signInViewController)
rootViewController = navigationController
}
Now your self.navigationController?.pushViewController(signUpVC, animated: true) will work since the "navigationController" exists.
how can i show specific viewcontroler in notification action?
in appdelegate
func usernotificationcenter(_center: UNUserNotification,didReceive response: UNNotificationPesponse,withCompletionHalndler completion Handler: #escaping () -> Void){
if identifer == "SHOWVIEW_ACTION":
//I want show specific viewController at this
}
func redirectToSpecificVC() {
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController: UIViewController = mainStoryboard.instantiateViewController(withIdentifier: "versesVC") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()}
used these function redirect to specific view controller
Add these Lines of code in did receive response, it worked for me
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController = self.window?.rootViewController as? UINavigationController
let destinationController = storyboard.instantiateViewController(withIdentifier: "YourIdentifierName") as? YourViewController
navigationController?.pushViewController(destinationController!, animated: false)
var presentedVC = self.window?.rootViewController
while (presentedVC!.presentedViewController != nil) {
presentedVC = presentedVC!.presentedViewController
}
In the scenario where you have a viewController which you want to present as root view above everything else what is the right way to do this?
let Storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let MY_VIEW = Storyboard.instantiateViewControllerWithIdentifier("VIEWID")
//Is this the right way?
UIApplication.sharedApplication().delegate.window?.rootViewController?.presentViewController(MY_VIEW, animated: true, completion: nil)
//Or this?
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(MY_VIEW, animated: true, completion: nil)
In other words why would you use UIApplication.sharedApplication().delegate.window? over UIApplication.sharedApplication().keyWindow?.rootViewController? and in what scenarios? What would be the pros/cons of using one or the other?
You can do as follow.
let rootController = storyboard.instantiateViewControllerWithIdentifier("VIEWID") as! SplashVC
if self.window != nil {
self.window!.rootViewController = rootController
}
advantage of this is not getting crash when window is nil.
Another way is that(Most secure way i think)
let navigationController:UINavigationController = storyboard.instantiateInitialViewController() as! UINavigationController
let rootViewController:UIViewController = storyboard.instantiateViewControllerWithIdentifier("VIEWID") as! LoginVC
navigationController.viewControllers = [rootViewController]
self.window?.rootViewController = navigationController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController: MainViewController = storyboard.instantiateViewControllerWithIdentifier("MainViewController") as! MainViewController
let rootViewController = self.window!.rootViewController as! UINavigationController
rootViewController.pushViewController(viewController, animated: true)
**How to get the presenting view controller in swift. I am using storyboard id.**And for presenting next viewcontroller how can I add to the hierarchy?
I tried like this:
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let logincontroller = mainStoryboard.instantiateViewControllerWithIdentifier("LoginViewController")
if UIApplication.sharedApplication().keyWindow?.rootViewController.presentingViewController == logincontroller {
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let nextVC = mainStoryboard.instantiateViewControllerWithIdentifier(storayboardIdentifier)
self.presentViewController(nextVC, animated: true, completion: nil)
}
In Appdelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if isLoggedin == false {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
return true
}
else {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("HomeViewController")
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
return true
}
}
The code works, it display the view but it still have a warning
Failed to instantiate the default view controller for
UIMainStoryboardFile 'Main' - perhaps the designated entry point is
not set?
Check initial entry points.
Also to present screen below code practice is better way.
let objNextViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ID_NextViewController") as? ViewController
self.presentViewController(objNextViewController, animated: true) { () -> Void in
print("Achive")
}
EDITED :
If you want to present viewcontroller from already presented view controller then use below code.
var currentViewController : UIViewController!
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
if let viewControllers = appDelegate.window?.rootViewController?.presentedViewController
{
self.currentViewController = viewControllers as UIViewController
}
else if let viewControllers = appDelegate.window?.rootViewController?.childViewControllers
{
self.currentViewController = viewControllers.last! as UIViewController
}
let objNewViewController : NewViewController!
self.currentViewController.presentViewController(objNewViewController!, animated: true, completion: { () -> Void in
NSLog("Achived")
})
try this code for presentingViewControllerusing storyboard name:
let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController") as UIViewController
self.presentViewController(viewController, animated: false, completion: nil)
try this code,
let storyboard : UIStoryboard = UIStoryboard(name:"Main", bundle: nil)
let vc : WelcomeViewController = storyboard.instantiateViewControllerWithIdentifier("WelcomeID") as WelcomeViewController
let navigationController = UINavigationController(rootViewController: vc)
self.presentViewController(navigationController, animated: true, completion: nil)
hope its helpful
My guess is that you haven't set an initial view controller for your storyboard.
To fix this open your storyboard and select the UIViewController you want to display as the first one. Then in the Attributes inspector tab(third one from the right in the utilities panel), look under the View Controller section and make sure the Is Initial View Controller checkbox is checked.
I'd like to navigate from one view controller to another. How can I convert the following Objective-C code into Swift?
UIViewController *viewController = [[self storyboard] instantiateViewControllerWithIdentifier:#"Identifier"];
UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:viewController];
[self.navigationController pushViewController:navi animated:YES];
Create a swift file (SecondViewController.swift) for the second view controller
and in the appropriate function type this:
let secondViewController = self.storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController
self.navigationController.pushViewController(secondViewController, animated: true)
Swift 2+
let mapViewControllerObj = self.storyboard?.instantiateViewControllerWithIdentifier("MapViewControllerIdentifier") as? MapViewController
self.navigationController?.pushViewController(mapViewControllerObj!, animated: true)
Swift 4
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "IKDetailVC") as? IKDetailVC
self.navigationController?.pushViewController(vc!, animated: true)
In my experience navigationController was nil so I changed my code to this:
let next = self.storyboard?.instantiateViewControllerWithIdentifier("DashboardController") as! DashboardController
self.presentViewController(next, animated: true, completion: nil)
Don't forget to set ViewController StoryBoard Id in StoryBoard -> identity inspector
If you don't want the back button to appear (which was my case, because I wanted to present after a user logged in) here is how to set the root of the nav controller:
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("YourViewController") as! YourViewController
let navigationController = UINavigationController(rootViewController: vc)
self.presentViewController(navigationController, animated: true, completion: nil)
SWIFT 3.01
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "Conversation_VC") as! Conversation_VC
self.navigationController?.pushViewController(secondViewController, animated: true)
In swift 4.0
var viewController: UIViewController? = storyboard().instantiateViewController(withIdentifier: "Identifier")
var navi = UINavigationController(rootViewController: viewController!)
navigationController?.pushViewController(navi, animated: true)
In Swift 4.1 and Xcode 10
Here AddFileViewController is second view controller.
Storyboard id is AFVC
let next = self.storyboard?.instantiateViewController(withIdentifier: "AFVC") as! AddFileViewController
self.present(next, animated: true, completion: nil)
//OR
//If your VC is DashboardViewController
let dashboard = self.storyboard?.instantiateViewController(withIdentifier: "DBVC") as! DashboardViewController
self.navigationController?.pushViewController(dashboard, animated: true)
If required use thread.
Ex:
DispatchQueue.main.async {
let next = self.storyboard?.instantiateViewController(withIdentifier: "AFVC") as! AddFileViewController
self.present(next, animated: true, completion: nil)
}
If you want move after some time.
EX:
//To call or execute function after some time(After 5 sec)
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
let next = self.storyboard?.instantiateViewController(withIdentifier: "AFVC") as! AddFileViewController
self.present(next, animated: true, completion: nil)
}
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let home = storyBoard.instantiateViewController(withIdentifier: "HOMEVC") as! HOMEVC
navigationController?.pushViewController(home, animated: true);
Swift 3
let secondviewController:UIViewController = self.storyboard?.instantiateViewController(withIdentifier: "StoryboardIdOfsecondviewController") as? SecondViewController
self.navigationController?.pushViewController(secondviewController, animated: true)
In swift 3
let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "NextViewController") as! NextViewController
self.navigationController?.pushViewController(nextVC, animated: true)
let objViewController = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
self.navigationController?.pushViewController(objViewController, animated: true)
Swift 5
Use Segue to perform navigation from one View Controller to another View Controller:
performSegue(withIdentifier: "idView", sender: self)
This works on Xcode 10.2.
Swift 4
You can switch the screen by pushing navigation controller first of all you have to set the navigation controller with UIViewController
let vc = self.storyboard?.instantiateViewController(withIdentifier: "YourStoryboardID") as! swiftClassName
self.navigationController?.pushViewController(vc, animated: true)
In AppDelegate you can write like this...
var window: UIWindow?
fileprivate let navigationCtrl = UINavigationController()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
self.createWindow()
self.showLoginVC()
return true
}
func createWindow() {
let screenSize = UIScreen.main.bounds
self.window = UIWindow(frame: screenSize)
self.window?.backgroundColor = UIColor.white
self.window?.makeKeyAndVisible()
self.window?.rootViewController = navigationCtrl
}
func showLoginVC() {
let storyboardBundle = Bundle.main
// let storyboardBundle = Bundle(for: ClassName.self) // if you are not using main application, means may be you are crating a framework or library you can use this statement instead
let storyboard = UIStoryboard(name: "LoginVC", bundle: storyboardBundle)
let loginVC = storyboard.instantiateViewController(withIdentifier: "LoginVC") as! LoginVC
navigationCtrl.pushViewController(loginVC, animated: false)
}
For Swift 4 & 5 Users can use this way
Swift 5
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewController(withIdentifier: "YourViewController") as! YourViewController
self.navigationController?.pushViewController(vc, animated: true)
Swift 4
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "YourViewController") as? YourViewController
self.navigationController?.pushViewController(vc!, animated: true)
Better practices Swift 5.0
More reusable and readable
Create a protocol
protocol Storyboarded { }
Now create an extension of the protocol
extension Storyboarded where Self: UIViewController {
static func instantiateFromMain() -> Self {
let storyboardIdentifier = String(describing: self)
// `Main` can be your stroyboard name.
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
guard let vc = storyboard.instantiateViewController(withIdentifier: storyboardIdentifier) as? Self else {
fatalError("No storyboard with this identifier ")
}
return vc
}
}
// Make a ViewController extension to use on all of your ViewControllers
extension UIViewController: Storyboarded {}
How to use
let vc = MyViewController.instantiateFromMain()
//here you can pass any data to the next view controller. for example we have a variable with name `myString` in our next view contoller and we want to pass the data my `self viewController`
vc.myString = "This string passed by MyViewController"
self.navigationController?.pushViewController(vc, animated: true)
Note:- You need to give the same identifier on the storyboard as your UIViewController class has. Check the example below
Update for Swift 5:
let next = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.present(next, animated: true, completion: nil)
Don't forget to update the Storyboard ID for both View Controllers!