LaunchScreen.storyboard not opening Main.storyboard Navigation Controller - ios

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)
}
}
}

Related

Limit Navigation Controller to a set of UIViewControllers

Is it possible to limit the NavigationController to a certain set of UIViewControllers. See the image, I want the navigation controller, but only for the login/create user session. Once logged in, I obviously don't want the user to be able to go back (except log out). How can I accomplish that? I can't figure it out.
Go to Storyboard -> select NavigationController -> Attributes Inspector -> uncheck "Shows Navigation Bar" property
Then select relationship between Login/SignUp And TabBarController, and delete it.
Once login you can set your TabBarController(or any related controller) as the rootViewController. And when app launches you can check it in your AppDelegate.swift file like this,
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if Auth.auth().currentUser != nil {
let tabBarController = storyboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! TabBarController
self.window?.rootViewController = tabBarController
self.window?.makeKeyAndVisible()
}
else
{
let loginNavController = storyboard.instantiateViewController(withIdentifier: "LoginNavController") as! UINavigationController
self.window?.rootViewController = loginNavController
self.window?.makeKeyAndVisible()
}
return true
}
You could change the stack of your navigation controller when needed, e.g:
func logIn() {
//Delete all presented view controllers up to a point
navigationController.setViewControllers([], animated: false)
//Create new view controller
let viewController = ....
navigationController.pushViewController(viewController, animated: true)
}
Take a look at setViewControllers, that may give you some idea.
If you want to leave previous view controllers in stack, and just forbid user to pop to them, then it may be the best solution to subclass UINavigationController and override func popViewController(animated: Bool) -> UIViewController?

Programmatically connecting Storyboard w/ ViewController

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.

Swift: Presenting a viewController over UITabBarController

After my app is finished launching, I want my app to open a LoginViewController if a current user is not logged in. Otherwise, it goes to a ViewController which is at the index of 0 (default value). So if not logged in, then show a modal view over UITabBarController.
My initial viewcontroller is a UITabBarController, which is created in UIStoryboard. It is check-marked as Initial View and is connected to other five viewcontrollers graphically.
Actually, I think I know the reason why a LoginViewController does not appear. It is because the value result was nil (I checked in the console). But why?? Other than this behavior, everything is working well. UITabBarController seems to be working without any problem.
My LoginViewController is embedded with UINavigationViewController. This code is inside a ViewController which is at a selected index of 0. It is put inside viewDidAppear() method
let main = UIStoryboard(name: "Main", bundle: nil)
let view = main.instantiateViewController(withIdentifier: "login") as! LoginViewController
let nav = UINavigationController(rootViewController: view)
self.tabBarController?.present(nav, animated: false, completion: nil)
However, it is not working.
Change the rootViewController of window to UINavigationController if user is not logged in and if he is change it to the tabBarController
You should implement the logic of deciding what is the desired initial view controller to be the root (based on whether the user logged in or not) before navigating to any view controller, application(_:didFinishLaunchingWithOptions:) method would be appropriate for such an implementation:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// let's assume that you are recognizing if the user logged in by a flag called 'isLoggedin':
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if isLoggedin { // show display main view controller
let mainViewController = storyboard.instantiateViewController(withIdentifier: "mainViewController")
// setup any needed config for mainViewController...
self.window?.rootViewController = mainViewController
} else { // display login view controller
let loginViewController = storyboard.instantiateViewController(withIdentifier: "loginViewController")
// setup any needed config for loginViewController...
self.window?.rootViewController = loginViewController
}
self.window?.makeKeyAndVisible()
return true
}
Or as a shorter version:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let isLoggedin = false
let initialViewController = storyboard.instantiateViewController(withIdentifier: isLoggedin ? "mainViewController" : "loginViewController")
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
return true
}
At this point, after the app finished launching (without jumping to any view controller yet), the initial view controller would be displayed based on the user logging in validation. I would assume that it would be a better behavior unless there is a specific requirement to present the login view controller on the of the tabbar controller; Logically, there is no even need to navigate to the tabbar controller if the user didn't login yet.
I run your code, Here is issue:
2018-01-18 16:23:52.845273+0800 try[8180:315334]
Warning: Attempt to present on whose view is not in the window hierarchy!
self.tabBarController?.present(nav, animated: false, completion: nil)
this line is very wired. I seldom saw.
So you can also let a child controller of self.tabBarController to present nav.
The choose one of the tab bar.

Error trying to programmatically add a controller file to a navigation controller without creating storyboard

I am trying to push a view controller onto a navigation controller without designing a storyboard. Is this possible as I am new to Swift? I created my navigation controller in appdelegate.swift file:
let viewController: UIViewController = ViewController(nibName: nil, bundle: nil)
viewController.view.backgroundColor = UIColor.white
let navController: UINavigationController = UINavigationController(rootViewController: viewController)
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.backgroundColor = UIColor.darkGray
self.window?.rootViewController = navController
self.window!.makeKeyAndVisible()
And now in ViewController.swift file, when the user clicks the button below is the file when I try to add the signInController:
func SignIn(sender: UIButton!) {
print("Im, here")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "signInController") as! signInController
self.navigationController?.pushViewController(controller, animated: true)
}
Below is my Error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Storyboard (<UIStoryboard: 0x608000269600>) doesn't contain a view controller with identifier 'signInController''
Question:
Do I have to create a storyboard and go to the inspector Id to add the storyboard Id? But what if I wanted to accomplish this without creating a storyboard file?
If you are not using a storyboard then you shouldn't attempt to create your sign-in view controller from a storyboard. Change the code to create the view controller directly:
func SignIn(sender: UIButton!) {
print("I'm, here")
let controller = signInController()
self.navigationController?.pushViewController(controller, animated: true)
}
BTW - you need to rename everything to follow standard naming conventions. Classnames should start with uppercase letters. All method, variable, and parameters names should start with lowercase letters.
Well you want to create a viewController without using the storyboard and you're still trying to use the storyboard in your code.
Try something like this instead:
var window: UIWindow?
var navigationController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
self.window = UIWindow(frame: UIScreen.main.bounds)
// Override point for customization after application launch.
self.window!.backgroundColor = UIColor.white
self.window!.makeKeyAndVisible()
let myViewController: YourNewViewController = YourNewViewController()
self.navigationController = UINavigationController(rootViewController: myViewController!)
self.window!.rootViewController = self.navigationController
return true
}

Going to another ViewController in closure does not work in swift

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)

Resources