Swift / How to set (and load) a new root view controller - ios

I have a RootVC. It is a SwipeViewController: UINavigationController.
SwipeViewController
Within WelcomeNavigationController, I'm checking a condition. If that condition is true, I want to open the RootVC.
class WelcomeNavigationController: UINavigationController {
override func viewWillAppear(animated: Bool) {
backendless.userService.setStayLoggedIn(true)
if backendless.userService.currentUser != nil {
print("currentUser != nil")
dispatch_async(dispatch_get_main_queue()) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc : RootVC = storyboard.instantiateViewControllerWithIdentifier("RootVC") as! RootVC
appDelegate.window?.rootViewController = vc
appDelegate.window?.makeKeyAndVisible()
}
}
}
The print statement is executed and some print statements from my RootVC are executed as well. So in theory the RootVC is loaded. But it is not visible. How can I make my RootVC visible?
Help is very appreciated.

I think you should be add rootVC to current view controller
self.addChildViewController(vc)
vc.view.frame = self.view.frame
self.view.addSubview(vc.view)
vc.didMoveToParentViewController(self)

Related

Navigation Bar disappears when going back to Home VC from a Details VC from Push Notification

If I click on the push notification, it takes me to the details page but when I click on the programatically created back button, it takes me back to HomeVC but with the top nav bar gone. Here is my app delegate function where I present the details VC :
private func gotoDetailsVC(value : String)
{
guard let window = UIApplication.shared.connectedScenes.filter({$0.activationState == .foregroundActive
|| $0.activationState == .background || $0.activationState == .foregroundInactive}).compactMap({$0 as? UIWindowScene}).first?.windows.filter({$0.isKeyWindow}).first
else { return }
let storyboard = UIStoryboard(name: "Home", bundle: nil)
let detailsVC = storyboard.instantiateViewController(identifier: "viewDeviceDataController") as viewDeviceDataController
detailsVC.deviceID = value
detailsVC.didComeFromPN = true
let navController = UINavigationController(rootViewController: detailsVC)
navController.modalPresentationStyle = .popover
// you can assign your vc directly or push it in navigation stack as follows:
window.rootViewController = navController
window.makeKeyAndVisible()
}
And here is my back button on the DetailsVC :
#objc func backAction() {
let storyboard = UIStoryboard.init(name: "Home", bundle: nil)
let homeVC = storyboard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
homeVC.modalPresentationStyle = .automatic
present(homeVC, animated: true, completion: nil)
}
How can I resolve this issue? Please help.
Resolved by navigating to root Navigation Controller.
#objc func backAction() {
let storyboard = UIStoryboard.init(name: "Home", bundle: nil)
let homeVC = storyboard.instantiateViewController(withIdentifier: "navControllerHome") as! UINavigationController
homeVC.modalPresentationStyle = .fullScreen
present(homeVC, animated: true, completion: nil)
}
Usually back action would just return to a previous state, but here it seems you are recreating the view.
I would not make private func gotoDetailsVC(value : String) replace rootViewController and instead make it be presented by current navigationController then on your back action you would not create a new home but dismiss your detail screen.

Push view controller from a Child View controller

From a button action, I have presented a viewController, from that presented VC, add another button and on that button action added a childView like this -
let vc = UIStoryboard(name: "Profile", bundle: nil).instantiateViewController(withIdentifier: "APPopUpViewControllerViewController") as! APPopUpViewControllerViewController
self.addChild(vc)
vc.view.frame = self.view.frame
self.view.addSubview(vc.view)
vc.didMove(toParent: self)
All are working perfectly till now, but when i try to push a viewController from this child VC , it does not work. How can i push a viewController from a childView ??
Push viewController code -
let storyboard = UIStoryboard(name: "WPLogin", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "WPSigninViewController") as! WPSigninViewController
self.navigationController?.pushViewController(vc, animated: true)
My guess is that self.navigationController is nil, so your optional chaining is failing.
You can try
print(parent)
print(parent?.navigationController)
self.parent?.navigationController?.pushViewController(vc, animated: true)
How do you present your ViewController ? If You have pushed it to a navigation controller, then your code is perfect and it should work I think.
If not, then try to present the WPSigninViewController modally as
let storyboard = UIStoryboard(name: "WPLogin", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "WPSigninViewController") as! WPSigninViewController
self.present(vc, animated: true)
and It should work.
If ViewController is your rootController, you can add a NavigationController to it :
let viewController = ViewController(nibName: nil, bundle: nil)
let navigationController = UINavigationController(rootViewController: viewController)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()

Back to root view controller in swift

Is there any way to dismiss all the view controllers ( in which some controllers are pushed through navigation) and go back to the root view controller. I saw lots of examples but they didn't work in my application. I am using swift 4
This is code in appdelegate
func setNavigationToRootViews(){
storyBoard = UIStoryboard(name: "Main", bundle: nil)
nav = storyBoard?.instantiateViewController(withIdentifier: "mainNavigation") as! UINavigationController?
let accessToken: String? = KeychainWrapper.standard.string(forKey: "token")
print(accessToken as Any)
if accessToken != nil {
let homeVc = storyBoard?.instantiateViewController(withIdentifier: "Home-VC") as! HomeViewController
nav?.pushViewController(homeVc, animated: false)
}else{
let welcomVc = storyBoard?.instantiateViewController(withIdentifier: "login-VC") as! LoginViewController
nav?.pushViewController(welcomVc, animated: false)
}
let leftMenuVC = storyBoard?.instantiateViewController(withIdentifier: "menu-VC") as! MenuViewController
container = MFSideMenuContainerViewController.container(withCenter: nav, leftMenuViewController: leftMenuVC, rightMenuViewController: nil)
container?.panMode = MFSideMenuPanModeNone
window?.rootViewController = container
window?.makeKeyAndVisible()
}
and this in my last View controller
#IBAction func okayBtnTapped(_ sender: Any) {
_ = self.navigationController?.popToRootViewController(animated:
true)
dismiss(animated: true, completion: nil)
}
Try this one:
self.navigationController?.popToRootViewController(animated: true)
You can just set a new RootController like this:
let storyboard = UIStoryboard(name: "sName", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "<YOUR ROOT CONTROLLER>")
self.window?.rootViewController = viewController
If you don't have a window at self.window you need to instanciate one based on your AppDelegate.
If you're within a NavigationController you can also use the answer of #Anshul Bhatheja
Check you navigation controller, and view controllers inside that navigation controller. It seems debugging issue.
this method popToRootViewController(animated:) is the method should work
If you are working with simple push navigation controller and you want to go back to root view controller then you simply have to write
_ = self.navigationController?.popToRootViewController(animated: true)
extension UINavigationController {
func popToViewController(ofClass: AnyClass, animated: Bool = true) {
if let vc = viewControllers.filter({$0.isKind(of: ofClass)}).last {
popToViewController(vc, animated: animated)
}
}
func popViewControllers(viewsToPop: Int, animated: Bool = true) {
if viewControllers.count > viewsToPop {
let vc = viewControllers[viewControllers.count - viewsToPop - 1]
popToViewController(vc, animated: animated)
}
}
}
pop to SomeViewController class
navigationController?.popToViewController(ofClass: SomeViewController.self)
pop 2 view controllers
navigationController?.popViewControllers(viewsToPop: 2)

Load view controller from another controller

I am using this code to load view controllers inside a container in ViewController without using any segue.
Example:
let newViewController =self.storyboard?.instantiateViewController(withIdentifier: "FBViewController")
newViewController!.view.translatesAutoresizingMaskIntoConstraints = false
self.cycleFromViewController(oldViewController: self.currentViewController!, toViewController: newViewController!)
self.currentViewController = newViewController
This code is working for any controller but in one case where i need to load a controller called FBViewController and in its ViewDidLoad i check if the user is logged, in case he is i want to load another controller instead of theFBViewController immediately.
I tried this code inside the viewDidLoad of FBViewController:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "userProfilePage")
self.present(controller, animated: true, completion: nil)
I tried calling a method in the main view controller to load the wanted view:
let otherViewController: ViewController = ViewController()
otherViewController.loggedIn()
Method:
func loggedIn(){
let newViewController = self.storyboard?.instantiateViewController(withIdentifier: "userProfilePage")
newViewController!.view.translatesAutoresizingMaskIntoConstraints = false
self.cycleFromViewController(oldViewController: self.currentViewController!, toViewController: newViewController!)
self.currentViewController = newViewController
}
None of them worked. I appreciate any help.
You can try the following inside MainViewController:
var newViewController : UIViewController
if loggedIn == true {
newViewController = self.storyboard?.instantiateViewController(withIdentifier: "userProfilePage")
} else {
newViewController = self.storyboard?.instantiateViewController(withIdentifier: "FBViewController")
}
newViewController!.view.translatesAutoresizingMaskIntoConstraints = false
self.cycleFromViewController(oldViewController: self.currentViewController!, toViewController: newViewController!)
self.currentViewController = newViewController
The code you have should work, just go to your storyboard and select the controller you want to call. Then make sure that under storyboard ID you have the same name that you are using to call it.

How can I show ViewController in UITabBarController?

I have a UITabBarController and all my other view controllers are connected to it. Now I want to show one my controller as:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ViewController = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! ViewController
but when I tried to:
let rootViewController = self.window?.rootViewController as! UINavigationController
rootViewController.pushViewController(vc, animated: true)
it gave me the next error:
Could not cast value of type 'UITabBarController' (0x1a899b818) to 'UINavigationController'
Later I've tried to do:
let rootViewController = self.window?.rootViewController as! UITabBarController
but in this case I get
UITabBar has no member pushViewController
How can I show/push my ViewController so it will appear with UINavigationBar and inside of UITabBar?
You need to place each of your view controllers inside a navigation controller.
E.g. currently you have a TabBarViewController
and two view controllers:
ViewControllerA
ViewControllerB
What you need to do is to embed each of them inside a navigation controller so you would have:
UINavigationController -> ViewControllerA
UINavigationController -> ViewControllerB
In order to push a new controller you would do:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ViewController = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! ViewController
let navViewController = myTabBar.selectedViewController as? UINavigationController
navViewController?.pushViewController(vc, animated: true)
Could not cast value of type 'UITabBarController' (0x1a899b818) to 'UINavigationController'
Your root view controller is of type UITabBarController.
Therefore you have to use the appropriate methods of this class and not UINavigationController.
To set view controllers use either
var viewControllers: [UIViewController]? or
func setViewControllers([UIViewController]?, animated: Bool).
To have a navigation bar you have to instantiate a UINavigationController and add your view controller to this navigation controller.
Then add your navigation controller to your UITabBarController with via one of the above options.
If your class is UITabBarController
You can add this:
private func showViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateViewController(withIdentifier: "queueTableViewController") as? QueueTableViewController else { return }
let navVC = self.selectedViewController as? UINavigationController
navVC?.pushViewController(vc, animated: true)
}
This will allow you to go to any VC
The second solution is something like this.
Here You will show the last VC and from there push some VC.
guard let tabCount = viewControllers?.count, tabCount > 1 else { return }
selectedIndex = tabCount - 1
if let navigationController = viewControllers?[selectedIndex] as? UINavigationController {
if let accountVC = navigationController.visibleViewController as? FirstViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateViewController(withIdentifier: "someViewController") as? SomeViewController else { return }
accountVC.navigationController?.pushViewController(vc, animated: true)
}
}

Resources