How to invoke Navigation controller form a VC - ios

I have 3 VC's (VC as in ViewController)
1 AuthViewController which is the entry point for the app and it checks if user is logged in else it displays the sign-in related views
2 HomeViewController is the main interface of the app landing page after the user has logged in
3 ListViewController which displays a list, invoked via a segue on HomeViewController.
I want to enable back navigation between ListViewController and HomeViewController via a Navigation Controller.
How can I achieve this If I have a Navigation controller whose root VC is HomeVC. how can I invoke it from my AuthVC so that it gets invoked with the Navigation controller.
I have tried to invoke the Navigation controller but did not work
I have also tried invoking the HomeVC by
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
if let viewController = mainStoryboard.instantiateViewController(withIdentifier: "mainViewController") as? UIViewController {
viewController.modalPresentationStyle = .overCurrentContext
self.present(viewController, animated: true, completion: nil)
although I was able to invoke the HomeVC could not get the navigation buttons
How can I invoke HomeVC from the AuthVC without loosing the NavigationController

You need 1 navigation controller, not 3.
If you want to push a new controller, then use push method, not present.
The back button gets enabled automatically on the navigation bar if there are controllers in the navigation stack and the current controller is not a root view controller.
When coming from AuthViewController to HomeController, you should change the RootViewController. So that the user cant go back to the Auth screen. That would make no sense.
Once you are on HomeController, you can push other controller using the below code and it will add a back button to your controller automatically.
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
if let viewController = mainStoryboard.instantiateViewController(withIdentifier: "mainViewController") as? UIViewController {
self.navigationController?.pushViewController(vc, animated: true)
}
You can also use the below extension:
extension UIViewController {
func pushVC(storyboardName : String, vcname : String) {
let vc = UIStoryboard.init(name: storyboardName, bundle: Bundle.main).instantiateViewController(withIdentifier: vcname)
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)
}
func popVC() {
self.navigationController?.popViewController(animated: true)
}
func makeRootVC(storyBoardName : String, vcName : String) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let vc = UIStoryboard(name: storyBoardName, bundle: Bundle.main).instantiateViewController(withIdentifier: vcName)
let nav = UINavigationController(rootViewController: vc)
nav.navigationBar.isHidden = true
appDelegate.window?.rootViewController = nav // If using XCode 11 and above, copy var window : UIWindow? in your appDelegate file
let options: UIView.AnimationOptions = .transitionCrossDissolve
let duration: TimeInterval = 0.6
UIView.transition(with: appDelegate.window!, duration: duration, options: options, animations: {}, completion: nil)
}
}

Related

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

Present view controller and allow user to swipe back

Currently, I'm presenting a new view controller like this:
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
if let viewController = mainStoryboard.instantiateViewController(withIdentifier: "MyViewController") as? MyViewController {
// Present fullscreen
viewController.modalPresentationStyle = .fullScreen
self.present(viewController, animated: true, completion: nil)
}
However, this doesn't let me swipe from the left to go back to the previous controller. How can I enable this behaviour?
Update
Here is my code now:
#objc func onNextButtonTapped(sender: UITapGestureRecognizer) {
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
if let viewController = mainStoryboard.instantiateViewController(withIdentifier: "MyViewController") as? MyViewController {
self.navigationController?.pushViewController(viewController, animated:true)
}
}
You need to push view controller to the navigational stack for that behaviour.
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
if let viewController = mainStoryboard.instantiateViewController(withIdentifier: "MyViewController") as? MyViewController {
navigationController?.pushViewController(viewController, animated: true)
}
For this to work the view controller that you're putting this code in must be embedded in a UINavigationController. For embedding the view controller into a navigation controller make sure the selected view controller does have the class that your code is contained in.
And after embedding it should look like this:

Present and dismiss view modally in tabbed application using swift

I've created a tabbed application and am able to present a view modally using the code below, however, I'm stuck on dismissing the view and displaying the tab bar and first view controller.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let signUpViewController = storyboard.instantiateViewController(withIdentifier:"SignUpViewController") as! SignUpViewController
self.window?.makeKeyAndVisible()
self.window?.rootViewController = signUpViewController
Inside that signUpViewController do this after giving that tabBarController a storyboard identifier in IB
let tab = storyboard!.instantiateViewController(withIdentifier:"tab")
UIApplication.shared.keyWindow?.rootViewController = tab
OR
(UIApplication.shared.delegate as! AppDelegate).window?.rootViewController = tab
You should present your modal vc over you tab bar vc
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let signUpViewController = storyboard.instantiateViewController(withIdentifier:"SignUpViewController") as! SignUpViewController
signUpViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen
signUpViewController.modalTransitionStyle = UIModalTransitionStyle.coverVertical
self.window.rootViewController.present(myModalViewController, animated: true, completion: nil)
(if self.window.rootViewController - your tab bar view controller)
And in this case you can use dismiss(animated: true, completion: nil) method in modal vc to dismiss it.
In your TabBarViewController, do the following when presenting SignUpViewController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier:"SignUpViewController")
viewController.modalPresentationStyle = .overFullScreen
self.presentViewController(viewController, animated:true)
and when dismissing, simply call the dismiss code from SignUpViewController
e.g. dismissViewControllerAnimated

Load all views related to tab bar controller by hard code

I would like to jump from a viewController to the first viewController related to Tab Bar Controller through code.
The tabBarController Scene has storyboard id tabView.
I'm working on this way:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: UITabBarController!
storyboard.instantiateViewController(withIdentifier: "tabView")
vc=storyboard.instantiateViewController(withIdentifier: "tabView") as! UITabBarController
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.present(vc as! UIViewController, animated: true, completion: nil)
}
But it loads only the first viewController (out of 5) without the tab bar related to. How can I solve it?
Oh, this code looks so wrong.
In your storyboard give and "tabView" ID to the TabBarController, not the ViewController inside it.
Why you are double instantiating ViewController? just do it once and assign it to the vc variable.
Why you've created delay before presenting the VC? It's some sort of workaround of something?
Working code:
let vc = storyboard.instantiateViewController(withIdentifier: "tabView") as! UITabBarController
self.present(vc, animated: true)
Use
(1)if you want to navigate from Appdelegate
let appDelegate = UIApplication.sharedApplication.delegate as! AppDelegate
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let tabBar = mainStoryboard.instantiateViewControllerWithIdentifier("TabBarController") as! TabBarController
appDelegate.window?.rootViewController = tabBar
appDelegate.window?.makeKeyAndVisible()
(2)if you want to navigate from viewcontroller which has root of navigation
self.navigationController?.pushViewController(tabBar, animated: true)

Swift: Prevent flicker when presenting login screen at startup

We have two storyboards in our app. The default view in storyboard #1 assumes the user has previously supplied valid credentials. If they have not done so we "redirect" to the login screen in storyboard #2:
let storyboard = UIStoryboard(name: "Authentication", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("LoginController") as UIViewController
presentViewController(controller, animated: true, completion: nil)
This works - but there is a flicker when this code runs where the default view briefly appears prior to the login view being displayed. How can you perform this action without a flicker?
Instead of presenting LoginController you have to set the respective ViewController as rootviewcontroller to the window in Appdelegate itself as folllows:
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
//Check for credentials,if value available make LoginStatus as true
if LoginStatus == true{
//Change Storyboard name "Main" to your "storyboard #1" name.
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
//Change Viecontroller name "My_Offer" to your "DefaultViewController name" name.
let vc = mainStoryboard.instantiateViewControllerWithIdentifier("My_Offer") as UIViewController
let navigationController = UINavigationController(rootViewController: vc)
self.window!.rootViewController = navigationController
}else{
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Authentication", bundle: nil)
let vc = mainStoryboard.instantiateViewControllerWithIdentifier("LoginController") as UIViewController
let navigationController = UINavigationController(rootViewController: vc)
self.window!.rootViewController = navigationController
}
self.window!.makeKeyAndVisible()
This will prevent from displaying default view before presenting LoginController.
Or else
In storyboard #1 create a DummyViewController(UIViewController) and make it as InitialViewController and set a Background image(Your SplashScreen Image) to DummyViewController .In viewdidload check for credentials,If You have value
let storyboard = UIStoryboard(name: "storyboard #1", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("default view ") as UIViewController
presentViewController(controller, animated: true, completion: nil)
if no credentials go with your given code
let storyboard = UIStoryboard(name: "Authentication", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("LoginController") as UIViewController
presentViewController(controller, animated: true, completion: nil)
this too prevents flicker but it is not good way to do.I suggest First Method setting rootviewcontroller.This may help you.Give it a try.
My suggestion is to set launch screen viewcontroller as rootviewcontroller of the application until the all your deciding factor get executed. This is one of the finest approach for async operation.

Resources