UIApplication.sharedApplication().keyWindow?.rootViewController freezes my app - ios

At some point in my app I need to go back to my main (i.e. home) ViewController. So I need to travel backwards into my navigation stack which by now is full with ViewControllers and NavigationControllers. So I do the following:
// to dismiss the current VC
self.navigationController?.popViewControllerAnimated(true)
// to go back home
UIApplication.sharedApplication().keyWindow?.rootViewController = homeNC
homeNC is a global var pointing to a NavigationController containing a storyboard instance of my main "home" ViewController.
It works, but partially. Meaning, it does take me back to my home ViewController but it then becomes unresponsive to touches and scrolling. Nothing responds, neither the navigationBar buttons nor the TableViewCells inside the home NC/VC. Xcode doesn't crash, just sits there waiting.
I can't figure out why. Can anyone think what I'm missing here?
** EDIT **
Maybe it's because I am embedding a nested VC inside my main (home) VC. Here's the code and what else I tried since:
homeNC = self.navigationController!
homeVC = self
let discoverVC: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("DiscoverVC") as UIViewController
// embed discoverVC in this VC
self.addChildViewController(discoverVC)
self.view.addSubview(discoverVC.view)
discoverVC.didMoveToParentViewController(self)
// store self NC for using it by "thank you" VC to come back to home after payment
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
// navigates back but becomes unresponsive
appDelegate.window?.rootViewController = homeVC
// navigates back but becomes unresponsive
appDelegate.window?.rootViewController = homeNC
I also tried saving a reference to the appdelegate window with the following:
// Initialise sideMenu
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let sideMenu = SSASideMenu(contentViewController: UINavigationController(rootViewController: MainViewController()), leftMenuViewController: LeftMenuViewController())
window!.rootViewController = sideMenu
window!.makeKeyAndVisible()
// get a reference to appdelegate's window so that we can navigate back later
mainWindow = window!
but again it doesn't navigate back when I do:
appDelegate.window?.rootViewController = mainWindow.rootViewController

Try this. I will work for you.
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = mainVC
EDIT
I read out your edited question. Problem is occurring due to SSASideMenu. I updated my answer and I hope now it will work for you. You have to make two changes. First in AppDelegate.swift, make property of SSASideMenu like this:
var window: UIWindow?
var sideMenu: SSASideMenu!
Now your code will be changed to:
sideMenu = SSASideMenu(contentViewController: UINavigationController(rootViewController: MainViewController()), leftMenuViewController: LeftMenuViewController())
Now if you want to change the view-controller, you have to change the contentViewController of SSASideMenu like this:
func showDiscoverVC() {
//Instantiate ViewController
let discoverVC: UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("DiscoverVC") as UIViewController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.sideMenu.contentViewController = UINavigationController(rootViewController: discoverVC)
}

Related

When should I set rootViewController? it's very confused

Hi I am struggling with rootViewController I left some code fragment with an explanation below, please let me know.
If I do it like below, it works and every things fine.
private func presentLogin() {
log.info("presenting login..")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let vc = storyboard.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
vc.modalPresentationStyle = .fullScreen
appDelegate.window!.rootViewController = vc
present(vc, animated: false)
}
right after that if I execute the code below, ios shows nothing but white blank page..
private func presentMain() {
log.info("presenting main..")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let vc = storyboard.instantiateViewController(withIdentifier: "MainTabBarController") as! MainTabBarController
vc.modalPresentationStyle = .fullScreen
appDelegate.window!.rootViewController = vc
present(vc, animated: false)
}
but when I deleted the code
appDelegate.window!.rootViewController = vc
everything is fine.
In other words, the code below works only the first time.
appDelegate.window!.rootViewController = vc
why? what am I missing?, I don't understand..
It seems there are a lot of bugs concerning the exchange of the root view controller. Switching the root view controller is also a little uncommon way of "navigation". I would recommend a different approach:
Upon app startup, use a "launch" view controller. This is your root view controllers, and it stays to be your root view controller all the time
if login is required, present a login view controller
after successful login, dismiss the login view and go on
if logged in, present the main application's entry view

How to change a viewcontroller of a slidemenu?

I have 2 separate VC-s that each need to be displayed to the user depending on a state of the app. This is how I create my original slidemenu in AppDelegate, it works as intended:
let mainVC = MainNavVC.create()!
let leftMenuVC = LeftMenuVC.create()!
slideMenuController = SlideMenuController(mainViewController: mainVC, leftMenuViewController: leftMenuVC)
self.window?.rootViewController = slideMenuController
self.window?.makeKeyAndVisible()
But when I try to change the leftMenuVC the screen goes black, the menu button just stops working and tapGesture still shows the wrong menu. Here is a code I call to change the slidemenu:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.slideMenuController?.leftViewController = AnotherLeftMenuVC.create()!
appDelegate.window?.rootViewController = appDelegate.slideMenuController
I also tried an alternative approach to change the leftMenuVC:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.slideMenuController = SlideMenuController(mainViewController: MainNavVC.sharedInst!,
leftMenuViewController: AnotherLeftMenuVC.create()!)
appDelegate.window?.rootViewController = appDelegate.slideMenuController
appDelegate.window?.makeKeyAndVisible()
This will turn my screen black but tapGesture will at least show the correct leftMenu.
How to make this work ?
You can try this trick. I tested it in your library's sample project. This works but it's up to you to clean the code and make the unwrapping optionals and castings safer.
Basically the trick is to embed your leftController or rightController into a UINavigationController. And access such navigationController using the way you're trying to access the slideMenuController in the AppDelegate. And then replace the viewControllers of that navigationController.
In the viewDidLoad() of the MainViewController (I'm talking about the sample project, you should be familiar with this or relate your mainController here). I have this block of code that will be executed in 5 seconds ;) I use this approach when testing something, just to save time for the sake of demo.
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerCellNib(DataTableViewCell.self)
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5)) {
let appDel = UIApplication.shared.delegate as! AppDelegate
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rightViewController = storyboard.instantiateViewController(withIdentifier: "RightViewController") as! RightViewController
((appDel.window?.rootViewController as! ExSlideMenuController).leftViewController as! UINavigationController).viewControllers = [rightViewController]
}
}
and in the appDelegate, I embed the controller into a navigationController:
let slideMenuController = SlideMenuController(mainViewController:nvc, leftMenuViewController: UINavigationController(rootViewController: leftViewController), rightMenuViewController: rightViewController)
This should help you out.

How can we change rootview of navigationController? [duplicate]

This question already has answers here:
Set rootViewController of UINavigationController by method other than initWithRootViewController
(5 answers)
Closed 4 years ago.
Initially my app starts with a view controller(TabBarView) which I have set in AppDelegate file.
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let tabBarView = TabBarView()
navigationController = UINavigationController(rootViewController: tabBarView)
window?.rootViewController = navigationController
Later in an app, when few criteria are met, I want to change my rootController to a new one(MusicPage).
At the moment I've create a new NavigationController object to display my (MusicPage)viewController however I think that my (TabBarView)viewController might be running in background and consuming memory.
I would really appreciate if someone tells me how to change my initial rootViewController.
Thanks. :)
The answer can be found on
Set rootViewController of UINavigationController by method other than initWithRootViewController
let appdelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var homeViewController = mainStoryboard.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeViewController
let nav = UINavigationController(rootViewController: homeViewController)
appdelegate.window!.rootViewController = nav
Edited: SWIFT 4
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let yourVc = UIViewController() // your view controller
appDelegate.window?.rootViewController = yourVc
appDelegate.window?.makeKeyAndVisible()

RevealViewController not working after programmatically set a view in iOS / Swift

First of all, let me introduce what I'm trying to do. Second... I'm new to Swift/iOS development.
What I'm trying to do is:
When I open the APP, it loads the LoginVC. When the user logs in, the TableSelectionVC loads. If the user selects a cell, it goes to the Home screen with the selected values. If the user taps on the bell (blue arrows) the app should go to AlarmVC.
It should work, but it doesn't. The AlarmVC says that self.revealViewController() is nil. BUT If I go to alarmVC through the menu option (red arrows) it loads normally and the Menu is shown. Also, if I choose the option in green, it goes to the TableSelectionVC and if I tap the icon, it goes to alarmVC and it doesn't crash. The problem is if I go from LoginVC to TableSelectionVC and tap on the icon, the it will crash.
I think it is the way that I'm setting the views, instantiating a controller and setting the window.rootViewController.
In my AppDelegate I have the following functions:
func changeRootViewControllerToSWRevealViewController () {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "SWRevealViewController")
controller.modalTransitionStyle = .flipHorizontal
if let window = self.window {
window.rootViewController = controller
}
}
func changeRootViewControllerToPlantSelectionVC () {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "navPlantSelectionVC")
controller.modalTransitionStyle = .flipHorizontal
if let window = self.window {
window.rootViewController = controller
}
}
When the user logs in the app, the following function is executed:
static func goToPlantSelection() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
//there should be an alert error
return
}
appDelegate.changeRootViewControllerToPlantSelectionVC()
}
After that, the PlantSelectionVC is shown and if the user taps on an cell the appDelegate.changeRootViewControllerToSWRevealViewController() is executed and the HomeVC is shown.
If the user taps on the icon, it tries to go to the AlarmVC but it crashes, like I said. I think I'm doing something wrong with binding the SWRevealViewController with LoginVC and TableSelectionVC.
The following code in AlarmVC tries to execute:
static func setupMenuToggle(button: UIBarButtonItem, viewController: UIViewController) {
button.target = viewController.revealViewController()
viewController.revealViewController().rearViewRevealWidth = viewController.view.bounds.size.width * 0.9
button.action = #selector(SWRevealViewController.revealToggle(_:))
}
But is shows the error in the third line: found nil while unwrapping an Optional value
Can anyone help ?
I fixed it. I set the login screen as sw_front for the swRevealViewController and after that, when the user logs in I would change the swRevealViewController front view controller with
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "navPlantSelectionVC")
self.revealViewController().setFront(controller, animated: true)
It is working now.

Load Tab Bar Controller from AppDelegate

I have been trying to figure this out by myself, but couldn't.
I want to load a Tab Bar controller from my AppDelegate (After a successful Google Sign in to the application).
I read here how to load a ViewController from AppDelegate
Perform Segue from App Delegate swift
Code in the example:
// Access the storyboard and fetch an instance of the view controller
let storyboard = UIStoryboard(name: "Main", bundle: nil);
let viewController: MainViewController = storyboard.instantiateViewController(withIdentifier: "ViewController") as! MainViewController;
// Then push that view controller onto the navigation stack
let rootViewController = self.window!.rootViewController as! UINavigationController;
rootViewController.pushViewController(viewController, animated: true);
In this example, my TabBar needs both a name and identifier, from what I understood.
Can someone please explain this to me ? I can't find "identifier" on the Tab Bar Controller , only "Title".
Also, I don't have a navigation controller view in my app.
set storyboard ID here
and embed the firstViewController in IB in navigationController
let rootViewController = self.window!.rootViewController as! UINavigationController;
rootViewController.pushViewController(viewController, animated: true);
iOS 13 Update
From version. iOS 13,
The RootViewController will be initiated in scene function located at
SceneDelegate.swift instead of AppDelegate.swift file. which looks like this.
Example:
// Simple init
let mainSB: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let loggedInTabController = mainSB.instantiateViewController(identifier: "loggedInTabController")
self.window!.rootViewController = loggedInTabController

Resources