I am trying to go to another ViewController (that is part of a TabBarController) after some networking code has been executed (using AlamoFire, inside a closure). The name of my current VC is StartingVC. I want to go to another VC after my closure has validated some code. In my example, the code is met, I am 100% sure because I have a println() that indicates so, but it does not take me to the other VC.
My closure code is the following:
Alamofire.request(.POST, "http://localhost/test.php", parameters: dataToSend).responseJSON{
(request, response, data, error) in
self.showViewController(HomeViewController(), sender: nil)
I am aware that I need to use self inside a closure to refer to the actual VC I am working in. But the code does not work.
Do you know how to present/push/goto another VC using code? I am not using any Storyboard links to my HomeViewController because I had some problems before using a segue both programmatically and in the storyboard, that is why I decided to go for the code approach.
Thank you for you help in advance
Cheers!
If you do not want to set navigation controller with storyboard and unable to start with there, your appdelegate should be like:
class AppDelegate: UIResponder, UIApplicationDelegate {
//you must mark the window and navigationController variables as optional
var window: UIWindow?
var navigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
navigationController = UINavigationController()
var homeViewController: UIViewController = UIViewController()
self.navigationController.pushViewController(homeViewController, animated: false)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window.rootViewController = navigationController
self.window.backgroundColor = UIColor.whiteColor()
self.window.makeKeyAndVisible()
return true
}
}
Other than that on view controller you can push view controller with storyboard object as:
var firstViewController = self.storyboard!.instantiateViewControllerWithIdentifier("firstViewController") as! firstViewController
self.navigationController?.pushViewController(firstViewController, animated: false);
Present view controller:
let vc = ViewController() //change this to your class name
self.presentViewController(vc, animated: true, completion: nil)
Related
I've been SLOWLY learning how to build iOS applications with Swift and was sent this article by someone why has been doing it professionally: A Case For Using Storyboards on iOS. Although I have taken the approach of learning how to build iOS application programmatically, I do believe that storyboards have their place in iOS development. I'm trying to implement Marin Benčević idea by having a new storyboard for every view w/ its own ViewController. However, I'm having issues getting this connected. I continuously run into this same error:
Failed to instantiate the default view controller for UIMainStoryboardFile 'LoginViewController' - perhaps the designated entry point is not set?
Now my file structure is a little different as I want to keep my views separated from my controllers but its as shown:
LoginViewController.swift:
class LoginViewController: UIViewController {
override func viewDidLoad() {
self.view.backgroundColor = UIColor.green
let vc = LoginViewController.instance()
self.present(vc, animated: true, completion: nil)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
self.window = UIWindow(frame: UIScreen.main.bounds)
let loginStoryboard = LoginViewController()
self.window?.rootViewController = loginStoryboard
self.window?.makeKeyAndVisible()
return true
}
UIStoryboardExt.swift:
import UIKit
extension UIStoryboard {
func initialViewController<T: UIViewController>() -> T {
return self.instantiateInitialViewController() as! T
}
}
UIViewControllerExt.swift:
import UIKit
extension UIViewController {
class func instance() -> Self {
let storyboardName = String(describing: self)
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
return storyboard.initialViewController()
}
}
info.plist:
Main storyboard file base name: LoginViewController
All in all, I'm wondering how can I connect a UIViewController to a View.storyboard programmatically without having to use the property tab of the storyboard. I've tried so many different variations of how to do this but I cannot seem to figure it out.
Here was a StackOverflow Question that I also attempted to get working but could not:StackOverflow
EDIT
Along with what Christian Abella said, I did need to also set the class like of the storyboard object
This is doable and I do this in some of my projects.
But you need to do the following:
Remove all "As Initial View Controller" ticks form your storyboard files.
In the Apps' Settings go to General tab and clear the Main Interface from the Deployment Info
In the Apps's Setting go to Info tab and clear the 'Main storyboard file base name' property.
self.window = UIWindow(frame: UIScreen.main.bounds)
let main : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = main.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
self.window?.rootViewController = loginVC
self.window?.makeKeyAndVisible()
return true
And as #Losiowaty pointed out, your viewDidLoad will cause an endless loop so you need to remove that code the present the LoginViewController.
I am trying to present a second viewcontroller on top of the first at startup without dismissing the first. This post Swift 3 - loading multiple ViewControllers at launch certainly looks like it has the answer. It recommends:
Add in your main view controller
var secondViewController:UIViewController!
And in your viewDidLoad:
secondViewController: UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "yourIdentifier") as! SecondViewController
That's it. When you want to present it, use:
self.present(secondViewController, animated: true, completion: nil)
This third line works great if, for example, I attach it as an action to a button. However, it does not work if it is in viewDidLoad: of the first viewController. This is what I need.
How can I automatically present the second viewController on top of the first viewController at launch?
You need to do it at the proper place in your view controller. There are several methods that gets called when your view controller is presented, and they are meant for different tasks.
To present another view controller you should put it in viewWillAppear: or viewDidAppear:, as viewDidLoad: is too early in the presentation.
Read more about these methods here:
https://stackoverflow.com/a/5659007/4543629
I think this depends on what you are trying to do. If you want to push that ViewController at launch you could also try to present it from your AppDelegate in func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
Something like: (like i said it depends on your use case)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewController(withIdentifier: "yourIdentifier") as! SecondViewController
let navigationController = UINavigationController(rootViewController: SecondViewController)
self.window?.makeKeyAndVisible()
self.window?.rootViewController?.present(navigationController, animated: false, completion: {() -> Void in
mainMenuVC.tabBarControl = tabBarController
})
return true
}
You can go to AppDelegate and setup window or if you workong with storyboard drag arrow to ViewControler what you want
Yes, that first comment by LGP does the trick. Thank you. I present it here as an answer. I added this function to the first viewController:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.present(secondViewController, animated: true, completion: nil)
}
I did not look into the appropriateness of true or false in calling super.viewDidAppear(true) if anyone wishes to comment.
I have just started to develop a new application using Swift (newbie). I have
LaunchScreen.storyboard with just a image of my splash screen
I have a Main.storyboard with a Navigation Controller connected to two segues, Home and Registration.
In the ViewController.swift, inside the viewDidLoad I am deciding which segue to call
My Main.Storyboard does not have a rootViewController, I need to decide which viewController to display at run time.
if (Settings.hasRegistrationCompleted()) {
performSegue(withIdentifier: "Home", sender: nil)
} else {
performSegue(withIdentifier: "Registration", sender: nil)
}
My questions
I put a breakpoint on the first line if (Settings.has and the breakpoint never reaches here
LaunchScreen lasts only for 2 seconds (tested on my Simulator) how do I increase it
EDIT
I have Main set as the Main Interface on my project. I did a Clean build and tried again, did not work.
Also below is the Main.Storyboard
in here two things you need to identify
first
check your storyboard name Main.storyboard are attached properly in your Target -> general -> Deployment Info -> main Interface, for e.g like this
second
check your Intial VC connected with navigation Controller and ensure your Initial VC as Root controller
update answer
initially set Stroryboard ID and for each VC
there after change the Root controller in appdelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
// Override point for customization after application launch.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootViewController: UIViewController?
if (Settings.hasRegistrationCompleted()) {
rootViewController = storyboard.instantiateViewController(withIdentifier: "HomeVC")
}else
{
rootViewController = storyboard.instantiateViewController(withIdentifier: "RegistrationVC")
}
let navigation = UINavigationController(rootViewController: rootViewController!)
self.window?.rootViewController = navigation
self.window?.makeKeyAndVisible()
return true
}
There you must be have One View-Controller as a RootVieController of your NavigationController and it must be initialize with arow like following screenshot. From The Login you need to segue to two View Controller.
Like Following
So you need to check in LoginViewController that you are already logged in or not. Or you can segue to register
You can also Subclass UINavigationController and set it to the storyboard. Remove the segues (you don't need them)
Then
class ViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
if (Settings.hasRegistrationCompleted()) {
let homeVC = self.storyboard?.instantiateViewController(withIdentifier: "HomeVC")
self.setViewControllers([homeVC!], animated: false)
}
else {
let regVC = self.storyboard?.instantiateViewController(withIdentifier: "RegistrationVC")
self.setViewControllers([regVC!], animated: false)
}
}
}
UI flow:
AppDelegate
-->
LoginViewController (not in the storyboard)
-->
navigation controller (in the storyboard)
-->
PFQueryTableViewController (in the storyboard) named "OrdersVC"
This is the navigation controller with OrdersVC:
This is my AppDelegate:
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// ...
// initial VC
let VC = LoginViewController()
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window!.rootViewController = VC
window!.makeKeyAndVisible()
return true
}
The above works fine. Then, from LoginViewController, I am then trying to display my storyboard's initial VC which is a navigation controller hosting a PFQueryTableViewController. Note that LoginViewController is not in the storyboard.
let destVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("OrdersVC") as! UITableViewController
// will throw "unexpectedly found nil"
let navController = UINavigationController(rootViewController: destVC)
navController.pushViewController(destVC, animated: true)
// will throw "unexpectedly found nil"
self.presentViewController(navController, animated: true, completion: nil)
Problem is that in my PFQueryTableViewController's viewDidLoad and viewDidAppear the following statement is always nil:
// navitaionController is nil
self.navigationController!.navigationBar.translucent = false
So how can I correctly instantiate PFQueryTableViewController inside its navigation controller?
You are instantiating the OrdersVC instead of instantiating the navigation controller into which it is embedded and which is the "initial" view controller of your storyboard. Use instantiateInitialViewController instead of using the identifier.
let nav = storyboard.instantiateInitialViewController()
self.window!.rootViewController = nav
The reason for the confusion is that you are "unlinking" the initial view controller from the storyboard with your login controller. You have to add the initial view controller back to the main window.
I have set my initial ViewController as rootViewController in appDelegate because I don't use storyboard. It looks like this way:
var window: UIWindow?
var mainNavigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.mainNavigationController = UINavigationController()
var mainController: UIViewController? = TineLineViewController()
self.mainNavigationController!.pushViewController(mainController!, animated: true)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = mainController
self.window!.makeKeyAndVisible()
...
...
My application running and my TineLineViewController shows up.
I have in this class a UIButton which call this method:
func postLeft(_sender: AnyObject?)
{
println("go to secound view..")
let secondViewController = PostCreateController()
let appDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
self.navigationController?.presentViewController(secondViewController, animated: true, completion: nil)
appDelegate.window?.rootViewController = secondViewController
}
This way if I push the button the screen change and my secondViewController shows up, without animation...
If I try to change the view this way:
self.navigationController?.pushViewController(secondViewController, animated: true)
It's still don't have any animations and after the secoundViewController shows up my application crash with this message:
* Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'adding a root view controller as a child of view controller:'
* First throw call stack:
(
I don't know if is it a best way to set my rootviewController class in appDelagate and why can't navigate without adding this line to my potLeft function:
appDelegate.window?.rootViewController = secondViewController
Without this line I can see in my app consol, the secondViewController viewDidLoad method is called, but the controller not shows up, and I get this message to the cosole:
Warning: Attempt to present on whose view is not in the window hierarchy!
How to navigate between two view without use storyboard?
1) Set mainNavigationController to rootViewController
2) Use self.navigationController?.pushViewController(secondViewController, animated: true)
Explanation
Logically your root view controller is UINavigationController while you are setting TineLineViewController to app's delegate rootViewController property. That is why you're getting exception.
self.mainNavigationController = UINavigationController()
var mainController: UIViewController? = TineLineViewController()
self.mainNavigationController!.pushViewController(mainController!, animated: true)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
// ERROR is here
// self.window!.rootViewController = mainController
// your root view controller should be navigation controller
self.window!.rootViewController = mainNavigationController
self.window!.makeKeyAndVisible()
You can set the secondViewController as rootViewController of the navigationController with animation:
func postLeft(_sender: AnyObject?)
{
println("go to secound view..")
let secondViewController = PostCreateController()
self.navigationController?.setViewControllers([secondViewController], animated: true)
}
Hope this have helped you !