I have used SceneDelegate to change the rootviewcontroller after login action. It works fine but when I logout I am not able to perform navigation again.
Here is my code for Scenekit:
let status = UserDefaults.standard.bool(forKey: UserDefaultKeys.status)
var rootVC : UIViewController?
if(status == true){
rootVC = UIStoryboard(name: AppStoryBoardName.main, bundle: nil).instantiateViewController(withIdentifier: TabBarViewController.className) as? TabBarViewController
}else{
rootVC = UIStoryboard(name: AppStoryBoardName.main, bundle: nil).instantiateViewController(withIdentifier: LoginViewController.className) as? LoginViewController
}
guard let root = rootVC else {return }
let nav = UINavigationController(rootViewController: root)
window?.rootViewController = nav
My logout code:
appUserDefaults.set(false, forKey: UserDefaultKeys.status)
appUserDefaults.synchronize()
let loginVC = self.storyboard?.instantiateViewController(withIdentifier: LoginViewController.className) as? LoginViewController
let window = UIApplication.shared.windows.first
window?.rootViewController = loginVC
And my login Button Action: (It doesnt works after logout action)
guard let controller = self.storyboard?.instantiateViewController(withIdentifier: VerificationViewController.className) as? VerificationViewController else { return }
controller.mobile = phoneTextField.text ?? ""
self.navigationController?.pushViewController(controller, animated: true)
It is not working because there is an inconsistency in hierarchy of view-controllers on logout button action and in sceneKit.
In sceneKit code you are embedding your LoginViewController in navigation controller and then assign the navigation controller as the windows root view-controller.
let status = UserDefaults.standard.bool(forKey: UserDefaultKeys.status)
var rootVC : UIViewController?
if(status == true){
rootVC = UIStoryboard(name: AppStoryBoardName.main, bundle: nil).instantiateViewController(withIdentifier: TabBarViewController.className) as? TabBarViewController
} else{
rootVC = UIStoryboard(name: AppStoryBoardName.main, bundle: nil).instantiateViewController(withIdentifier: LoginViewController.className) as? LoginViewController
}
guard let root = rootVC else {return }
// Embedding the specific controller in navigation controller and assigning navigation controller as windows root.
let nav = UINavigationController(rootViewController: root)
window?.rootViewController = nav
In that case you will have navigation controller in LoginViewController and the login button action works perfect i.e.
self.navigationController?.pushViewController(controller, animated: true)
But on logout you simply assign your LoginViewController as the windows root i.e.
appUserDefaults.set(false, forKey: UserDefaultKeys.status) appUserDefaults.synchronize()
let loginVC = self.storyboard?.instantiateViewController(withIdentifier: LoginViewController.className) as? LoginViewController
let window = UIApplication.shared.windows.first
// loginVC is not embedded in Navigation Controller
window?.rootViewController = loginVC
After the above transition the LoginViewController will not navigation controller so the optional chaining of pushingViewController in login button action will fails i.e.
self.navigationController?.pushViewController(controller, animated: true)
Keep it consistent by embedding the LoginViewController in Navigation Controller even on logout, update your logout action code to :
appUserDefaults.set(false, forKey: UserDefaultKeys.status) appUserDefaults.synchronize()
let loginVC = self.storyboard?.instantiateViewController(withIdentifier: LoginViewController.className) as? LoginViewController
let window = UIApplication.shared.windows.first
// Embed loginVC in Navigation Controller and assign the Navigation Controller as windows root
let nav = UINavigationController(rootViewController: loginVC)
window?.rootViewController = nav
Related
Why I am getting an exception while navigating. I am a beginner for the iOS development. I am navigating once the user has log in. I have used following code. I don't understand what I am missing.
I got the following exeption at last line -
Fatal error: Unexpectedly found nil while unwrapping an Optional value
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyBoard.instantiateViewController(withIdentifier: "taskListController") as! TaskListController
self.navigationController!.pushViewController(secondViewController, animated: true)
Please let me know my mistake.
You must add a navigation controller first before you push a VC
Navigation controller using code
func goToMyVC(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let mainView = storyboard.instantiateViewController(withIdentifier: "yourVC") as? yourVC
{
self.window = UIWindow(frame: UIScreen.main.bounds)
let nav1 = UINavigationController()
nav1.isNavigationBarHidden = true //Show or hide nav bar
nav1.viewControllers = [mainView]
self.window!.switchRootViewController(nav1)
self.window?.makeKeyAndVisible()
} }
In your viewcontroller call this function,
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.goToMyVC()
Navigation controller from story
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "controller") as? controller
self.navigationController?.pushViewController(vc!, animated: true)
Try the blow codes,
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "taskListController") as? TaskListController
self.navigationController?.pushViewController(secondViewController!, animated: true)
and make sure that you embedded the login view controller in a navigation controller
To skip login if user logged already I am using user defaults and in app delegate I am calling home view controller but the problem is the data is not getting as error coming as nil error Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value in swreal view controller
here my code
if UserDefaults.standard.bool(forKey: "login") {
//YES Already Login
self.window = UIWindow(frame:UIScreen.main.bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
let navigationController = UINavigationController.init(rootViewController: viewController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
} else {
//NOT Login
self.window = UIWindow(frame:UIScreen.main.bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "signinpage") as! ViewController
let navigationController = UINavigationController.init(rootViewController: viewController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
and after skiped login to home nil value appearing in this 3rd line. self.view......
super.viewDidLoad()
menuButton.target = self.revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
#IBAction func logoutButton(_ sender: Any) {
let alert = UIAlertController(title: "Alert", message: "Are you Sure You want to Logout", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
GIDSignIn.sharedInstance().signOut()
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "signinpage") as! ViewController
UserDefaults.standard.set(false, forKey: "login")
self.navigationController?.setViewControllers([secondViewController], animated: true)
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler:nil))
self.present(alert, animated: true, completion: nil)
}
First check your storyboard Id, is that proper or not. And you can check below code for more information. Hope that it may help to solve your problem.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if UserDefaults.standard.object(forKey: "login") != nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController: HomeViewController = storyboard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
let mainViewController = storyboard.instantiateViewController(withIdentifier: "MainViewController") as! MainViewController
let navigationController = storyboard.instantiateViewController(withIdentifier: "NavigationController") as! NavigationController
navigationController.setViewControllers([viewController], animated: false)
mainViewController.rootViewController = navigationController
let window = UIApplication.shared.delegate!.window!
window!.rootViewController = mainViewController
UIView.transition(with: window!, duration: 0.3, options: [.transitionCrossDissolve], animations: nil, completion: nil)
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//Check that user login or not
if let isLogin = UserDefaults.standard.object(forKey: "login") as? Bool {
//If already login then show Dashboard screen
self.showDashboardScreen()
} else {
//If not login then show Login screen
self.showLoginScreen()
}
return true
}
func showLoginScreen() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController: LoginViewController = storyboard.instantiateViewController(withIdentifier: "#LoginViewControllerStoryboardID") as! LoginViewController
var navigationController = UINavigationController()
navigationController = UINavigationController(rootViewController: loginViewController)
//It removes all view controllers from the navigation controller then sets the new root view controller and it pops.
window?.rootViewController = navigationController
//Navigation bar is hidden
navigationController.isNavigationBarHidden = true
}
func showDashboardScreen() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let dashboardViewController: DashboardViewController = storyboard.instantiateViewController(withIdentifier: "#DashboardViewControllerStoryboardID") as! DashboardViewController
var navigationController = UINavigationController()
navigationController = UINavigationController(rootViewController: dashboardViewController)
//It removes all view controllers from the navigation controller then sets the new root view controller and it pops.
window?.rootViewController = navigationController
//Navigation bar is hidden
navigationController.isNavigationBarHidden = true
}
According to my observation,
self.revealViewController() is nil. To assure if this is nil, add a breakpoint at the 3rd line and print self.revealViewController() in the console by doing
po self.revealViewController()
if that is nil,
Refer below links:
Self.revealViewController() returns nil
https://github.com/John-Lluch/SWRevealViewController/issues/627
https://www.codebeaulieu.com/programming/2017/5/5/a-cleaner-swrevealviewcontroller-implementation-using-swift-extensions
Also, show the code that you've used to configure SWRevealcontroller.
change this code
self.window = UIWindow(frame:UIScreen.main.bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
let navigationController = UINavigationController.init(rootViewController: viewController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
to this:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "SWRevealViewController")
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
is suposed that you have a navigation controller of typeSWRevealViewController with the segues sw_front to your HomeViewControlleror a NavigationController before it and a sw_rear to your menu VC. so you must instantiate the SWRevealViewController to show the menu, in your code your goint direct to your home screen so the SWRevealViewController is nil because is not been instantiate before the HomeViewController.
PD: here is a sample how i dit in a project and works out the box with the above code
and set this in the identity inspector
You can use multi storyboard concept.
Login Storyboard
Home Storyboard
+(void) LoginHomeScreen:(NSString*)strController andIndex:(int)index
{
NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:USERDETAILS];
NSDictionary *userData = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (userData != NULL && [userData count] > 0) {
//DashboardVC
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Home" bundle:nil];
UINavigationController *navigationController = [storyboard instantiateViewControllerWithIdentifier:#"SideMenuNavigationVC"];
[navigationController setViewControllers:#[[storyboard instantiateViewControllerWithIdentifier:strController]]];
LGSideMenuController *mainViewController = [storyboard instantiateInitialViewController];
mainViewController.rootViewController = navigationController;
UIViewController * SideMenu = [storyboard instantiateViewControllerWithIdentifier:#"SideMenuVC"];
mainViewController.leftViewController = SideMenu;
mainViewController.leftViewPresentationStyle = LGSideMenuPresentationStyleSlideBelow;
UIWindow *window = UIApplication.sharedApplication.delegate.window;
window.rootViewController = mainViewController;
[UIView transitionWithView:window duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:nil];
} else {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
LoginVC *loginVC = (LoginVC *)[storyboard instantiateViewControllerWithIdentifier:#"LoginVC"];
UINavigationController *navLogin = [[UINavigationController alloc] initWithRootViewController:loginVC];
navLogin.navigationBar.hidden = true;
[[UIApplication sharedApplication].delegate.window setRootViewController:navLogin];
}
}
I have Side Menu this as SlideMenu. But I want it to start from my ShopHome screen. And in my app my root/initial VC is Login page. When I use the below code it goes directly to ShopHome. But instead I want my app to start from Login and then menu on ShopHome screen. I tried changing root to my LoginVC but then menu doesn't work when I'm on ShopHome page.
AppDelegate
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController = storyboard.instantiateViewController(withIdentifier: "ShopHomeVC") as! ShopHomeVC
let rightViewController = storyboard.instantiateViewController(withIdentifier: "RightMenuVC") as! RightMenuVC
let nvc: UINavigationController = UINavigationController(rootViewController: mainViewController)
let slideMenuController = SlideMenuController(mainViewController: nvc, rightMenuViewController: rightViewController)
self.window?.rootViewController = slideMenuController
self.window?.makeKeyAndVisible()
I checked Slide Menu
Use this and delegate add
let leftViewController = storyboard.instantiateViewController(withIdentifier: "LeftViewController") as! LeftViewController
let rightViewController = storyboard.instantiateViewController(withIdentifier: "RightViewController") as! RightViewController
let nvc: UINavigationController = UINavigationController(rootViewController: mainViewController)
let slideMenuController = SlideMenuController(mainViewController: nvc,leftMenuViewController: leftViewController, rightMenuViewController: rightViewController)
slideMenuController.automaticallyAdjustsScrollViewInsets = true
slideMenuController.delegate = mainViewController
self.window?.rootViewController = slideMenuController
self.window?.makeKeyAndVisible()
This is how I solved it. On my LoginButton action changed my root.
#IBAction func loginButtonAction(_ sender: Any) {
let mainViewController = storyboard.instantiateViewController(withIdentifier: "ShopHomeVC") as! ShopHomeVC
let rightViewController = storyboard.instantiateViewController(withIdentifier: "RightMenuVC") as! RightMenuVC
let nvc: UINavigationController = UINavigationController(rootViewController: mainViewController)
let slideMenuController = SlideMenuController(mainViewController: nvc, rightMenuViewController: rightViewController)
UIApplication.shared.delegate?.window?.rootViewController = slideMenuController
}
Thanks to #kaminara4 (GitHub)
override init() {
super.init()
parseLoginHelper = ParseLoginHelper {[unowned self] user, error in
// Initialize the ParseLoginHelper with a callback
if let error = error {
// 1
ErrorHandling.defaultErrorHandler(error)
} else if let _ = user {
// if login was successful, display the TabBarController
// 2
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController")
// 3
self.window?.rootViewController!.presentViewController(tabBarController, animated:true, completion:nil)
}
}
}
So I understand that the code here should be able to replace my login screen upon hitting login, however, it does not. Instead it loads and stays on the login screen for parse. However, if I exit the app, it loads the proper screen. Does anyone have any ideas how to dismiss login screen more efficiently upon login?
let user = PFUser.currentUser()
let startViewController: UIViewController;
if (user != nil) {
// 3
// if we have a user, set the TabBarController to be the initial view controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
startViewController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
} else {
// 4
// Otherwise set the LoginViewController to be the first
let loginViewController = PFLogInViewController()
loginViewController.fields = [.UsernameAndPassword, .LogInButton, .SignUpButton, .PasswordForgotten]
loginViewController.delegate = parseLoginHelper
//loginViewController.signUpController?.delegate = parseLoginHCelper
startViewController = loginViewController
}
// 5
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = startViewController;
self.window?.makeKeyAndVisible()
return false
}
I believe you should not call
self.window?.rootViewController!.presentViewController(tabBarController, animated:true, completion:nil)
instead if you already in some controller why just go to tabBar like that:
presentViewController(tabBarController, animated:true, completion:nil)
You should not use :
self.window?.rootViewController!.presentViewController(tabBarController, animated:true, completion:nil)
Try to change that line by :
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("tabBarController") as! tabBarController
self.presentViewController(vc, animated: true, completion: nil)
and in your storyboard, set the storyboard id of tabBarController under identity inspector to : tabBarController
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)