Swift - popViewController after Dismiss - ios

I'm trying to dismiss viewcontroller presented as modal("Present Modally") and then popViewController in navigationController. Here is my storyboard, I want to go from QRScanner VC to Monitoring VC.
Here is how I'm presenting QRScanner VC in Add Device VC:
let storyboard = UIStoryboard(name: StoryboardIDs.MainStoryboard, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: VCIDs.QRDeviceScannerVC) as! QRDeviceScannerVC
controller.deviceName = deviceNameTxt.text
present(controller, animated: false, completion: nil)
Here is how I'm trying to go back to MonitoringVC:
self?.navigationController?.popViewController(animated:true)?.navigationController?.popViewController(animated: false)
Also tried:
self?.dismiss(animated: true, completion: {
self?.presentingVC?.navigationController?.popViewController(animated: false)
})
It always goes to the Add Device VC instead of Monitoring VC

Use an unwind segue, with the unwind target method located in the Monitoring VC. All the right stuff will just happen as if by magic.

You can try with
self?.dismiss(animated: false, completion:nil)
self?.presentingVC?.navigationController?.popViewController(animated: true)
Just check if you have reference to the navigation controller

Related

Swift: Presenting VC in completion handler of dismissal

I have the following ViewController (VC) flow:
SplashScreen/launch VC -> Login VC (on notification, present) -> SplashScreenVC -> Main VC
I want to avoid using an unwind segue because I will need to regularly re-authenticate the user regardless of current VC and so would much rather programatically 'present'.
The problem is, I am able to present and dismiss the SplashScreen VC (which is originally the root) but then cannot do the same for the Login VC without an error.
Code:
//in SplashScreen VC viewDidAppear
let loginVC = myStoryboard.instantiateViewController(identifier: "loginVC") as UIViewController
loginVC.modalPresentationStyle = .fullScreen
loginVC.modalTransitionStyle = .coverVertical
//dismissal?
self.dismiss(animated: true, completion: {
self.present(loginVC, animated: true, completion: nil)
})
//in loginVC selector function
let launchVC = myStoryboard.instantiateViewController(identifier: "launchVC") as UIViewController
launchVC.modalPresentationStyle = .fullScreen
launchVC.modalTransitionStyle = .coverVertical
//check for top view controller (debugging)
print("TOPVC at LoginVC: \(self.getTopVC()!)")
//handle dismissal?
self.dismiss(animated: true, completion: {
self.present(launchVC, animated: true, completion: nil)
})
WARNING:
Warning: Attempt to present <Slidr.LaunchScreenViewController: 0x15be0ef90> on <Slidr.LoginViewController: 0x15be6b510> whose view is not in the window hierarchy!
Warning: Attempt to present <Slidr.TestingViewController: 0x15db00ac0> on <Slidr.LaunchScreenViewController: 0x15bd06960> whose view is not in the window hierarchy!
Code runs fine if I don't dismiss the loginVC but I would like to avoid residue controllers over time.
I've tried to present from the top VC rather than 'self' but that doesn't seem to change anything.
Any help would be much appreciated.
As error says you need to present a vc from a 1 that's currently dismissed , so instead do
self.dismiss(animated: true, completion: {
(UIApplication.shared.delegate as! AppDelegate).window?.rootViewController = loginVC
}
Another way also is to embed rootVC inside a navigationController and do
self.navigationController?.setViewControlls([loginVC],animated:true)

How to go back to the initial ViewController the right way?

I have a few view controllers with navigation bar and tab bar. Then I have an initial view controller with no navigation bar and tab bar. I want to go back to the initial view controller with no other view controllers in stack. Here's what I do:
// Head back to Initial View Controller.
let initialViewController = self.storyboard!.instantiateViewController(withIdentifier: "Initial")
UIApplication.shared.keyWindow?.rootViewController = initialViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = initialViewController
This works perfectly. But then it doesn't move to another view controller from the initial view controller again. For example:
#IBAction func loginButton(_ sender: Any) {
print("Login Button Tapped.")
let storyboard = UIStoryboard(name: "SignInViewController", bundle: nil)
let signInViewController = storyboard.instantiateViewController(withIdentifier: "SignInViewController") as! SignInViewController
self.navigationController?.show(signInViewController, sender: nil)
}
Here, the print statement is run, but self.navigationController.show won't. How do all this work? And what's the right approach to accomplish this?
EDIT:
To avoid any confusion, here's my storyboard. The left most viewcontroller (pink screen) is the initial view controller I want to go back to. The right most on the same level is the view where I want to go back from.
It doesn't move to another view controller, because you are setting a simple view controller in your rootViewController.
What you need is:
let initialViewController = self.storyboard!.instantiateViewController(withIdentifier: "Initial")
let navController = UINavigationController.init(rootViewController: initialViewController)
UIApplication.shared.keyWindow?.rootViewController?.present(navController, animated: true, completion: nil)
This will create a navigationviewcontroller, so when on "Initial" you should be able to perform push, present actions of another view controllers.
From your initial view controller, you need to instantiate your navigation stack and present it modally. Then, when you want to get back to your pre-navigation initial VC, just call dismiss(animated:,completion:), which will dismiss your entire navigation stack and return you to the initial view controller.
self.navigationController.popToRootViewController(animated:) is what you're looking for.
https://developer.apple.com/documentation/uikit/uinavigationcontroller/1621855-poptorootviewcontroller
EDIT
Try dismissing the navigationController after calling popToRoot.
self.navigationController.dismiss(animated: true, completion: nil)
EDIT
Try the following:
#IBAction func loginButton(_ sender: Any) {
print("Login Button Tapped.")
let storyboard = UIStoryboard(name: "SignInViewController", bundle: nil)
let signInViewController = storyboard.instantiateViewController(withIdentifier: "SignInViewController") as! SignInViewController
present(signInViewController, animated: true, completion: nil)
//self.navigationController?.show(signInViewController, sender: nil)
}
Then to dismiss the signInViewController call self?.navigationController.dismiss(animated:true, completion:nil) from inside the signInViewController.

Swift How to present view in root navigation after dismiss modal

I'm trying to make ViewController present after Modal dismiss
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let chatRoomVC = storyboard.instantiateViewController(withIdentifier: "ChatRoomVCId") as! ChatRoomVC
chatRoomVC.hidesBottomBarWhenPushed = true
chatRoomVC.passValue = passValue
self.dismiss(animated: true, completion: {
self.present(chatRoomVC, animated: true, completion: nil)
})
But it will return "whose view is not in the window hierarchy!" maybe it's present a view after the controller dismissed
Notice the self in self.present, what you are doing, is basically tell the vc that you are dismissing to present a new vc, thats wrong way to do, the correct way is tell it's PARENT vc to present a new vc, by using delegate/unwind to call the parent vc to present new vc
You are using self. It means you are dismissing the current view controller. It should be the parent view controller who will present a new view controller.
When present from "Controller_A" to "Controller_B"-> present it like given below
--- View Controller A ---
self.navigationController?.present(Controller_B, animated: true, completion: nil)
When you want to dismiss "Controller_B" and present "Controller_C" using Navigation controller
--- View Controller B ---
let presenController : UINavigationController = self.presentingViewController as! UINavigationController
presentingController.dismiss(animated: true, completion: {
presenController.pushViewController(Controller_C, animated: true)
})

Swift - Present another view controller with its navigation bar

I have two ViewControllers -- one with storyboard and one without. Both of those view controllers have their own Navigation Bar at the top. Now when I use self.presentViewController(editorViewController, animated: true, completion: nil) my editorViewController comes up but without its Navigation bar.
Any ideas how to fix this?
I fixed the problem using the following code:
let editorViewController = IMGLYMainEditorViewController()
let navEditorViewController: UINavigationController = UINavigationController(rootViewController: editorViewController)
self.presentViewController(navEditorViewController, animated: true, completion: nil)
I just added the navEditorViewController as it made my navigation bar with its items to appear.
Try self.navigationController!.pushViewController(...)
Swift 5+
let destinationNavigationController = self.storyboard!.instantiateViewController(withIdentifier: "nav") as! UINavigationController
destinationNavigationController.modalPresentationStyle = .fullScreen
self.present(destinationNavigationController, animated: true, completion: nil)
Here your navigation bar replaces with new navigation bar.
So for everyone still curious about this problem, given that we already have existing UINavigationController other than the current one:
Swift 3
First, we need to find the UIViewController that we want to present:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "DestinationViewController") as! DestinationViewController
Next, we're doing the same thing for UINavigationController:
let destinationNavigationController = storyboard.instantiateViewController(withIdentifier: "DestinationNavigationController") as! UINavigationController
Then, we want to bring the DestinationViewController to the top of our destination UINavigationController stack:
destinationNavigationController.pushViewController(destinationViewController, animated: true)
Finally, just present destination UINavigationController:
self.present(destinationNavigationController, animated: true, completion: nil)

How to switch view controllers in swift?

I'm trying to switch from one UIViewController to another using code. Right now I have,
self.presentViewController(ResultViewController(), animated: true, completion: nil)
All this is doing is taking me to a black screen instead of the ResultViewController. Any suggestions?
With Storyboard. Create a swift file (SecondViewController.swift) for the second view controller
and in the appropriate function type this:
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
self.navigationController.pushViewController(secondViewController, animated: true)
Without storyboard
let secondViewController = ViewController(nibNameOrNil: NibName, bundleOrNil: nil)
self.presentViewController(secondViewController, animated: true, completion: nil)
You can try this one
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let resultViewController = storyBoard.instantiateViewControllerWithIdentifier("ResultView") as ResultViewController
self.presentViewController(resultViewController, animated:true, completion:nil)
Make sure you set your view controller identifier.
Swift 3
To present a controller modally
let vc = self.storyboard!.instantiateWithIdentifier("SecondViewController")
self.present(vc, animate: true, completion: nil)
To show a controller
self.show(vc, sender: self)
Try this:
let vc = ViewController(nibNameOrNil: yourNibName, bundleOrNil: nil)
self.presentViewController(vc, animated: true, completion: nil)
Hope this helps.. :)
The easiest way to do this would be to create a segue by right clicking on the view you are starting on and dragging the blue line to the result view controller the hit the show segue option. Then set the identifier of the segue to "segue". Now add this code wherever you would like in your view controller:
self.performSegueWithIdentifier("segue", sender: nil)
this will trigger the segue that will go to your resultViewController
The easiest way to switch between screens in iOS is. Add a button on the current screen. Then press control and drag the button to the target screen. Then select push. And when you will tap the button the screen will be switched to the target screen.
Make sure you are using a UINavigationViewController.
Source: ios UINavigationController Tutorial.
Shaba's answer is a good start but requires the following so that you
can control the segue from within your view controller:
override func shouldPerformSegue(withIdentifier identifier: String,
sender: Any?) -> Bool {
// your code here
// return true to execute the segue
// return false to cause the segue to gracefully fail }
Source
vc = YourViewController()
UIApplication.shared.keyWindow?.rootViewController = vc
Or
vc = YourViewController()
UIApplication.shared.keyWindow?.rootViewController = UINavigationController(rootViewController: vc) //If you want to add navigation functionality to that VC

Resources