Pop a navigation controller - ios

I have two views, V1 and V2. I want to "present" V2 when the add button is pressed on V1, and "pop" V2 off when the stop button is pressed, so that the original V1 is the top of the stack.
From what I have read, I need a separate view controller for V2. From the limited information I could find, I need V1's view controller to conform to V2's protocol, V2delegate. This is what I have, but it is not working:
ViewController1 with V1
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout, FormViewControllerDelegate {
let form = FormViewController()
func addTapped() {
form.delegate = self
let nav = UINavigationController(rootViewController: form)
navigationController?.present(nav, animated: true)
}
func popForm() {
navigationController?.popViewController(animated: true)
navigationController?.popToViewController(self, animated: true)
print("popped")
}
}
ViewController2 with V2
class FormViewController: UIViewController {
var delegate: FormViewControllerDelegate?
func stopTapped() {
print("pop it")
delegate?.popForm()
}
}
protocol FormViewControllerDelegate {
func popForm()
}
What am I doing wrong here?

In your VC2, Change to use this code
func stopTapped() {
print("pop it")
self.dismiss(animated: true, completion: nil)
}

Use this in ViewController1 to present FormViewController
func addTapped() {
let nav = UINavigationController(rootViewController: form)
self.present(nav, animated: true)
}
Within FormViewController when want to dismiss use this
func stopTapped() {
self.dismiss(animated: true)
}

You have presented the ViewController not pushed the ViewController, so what you need is to dismiss the Controller instead of pop the controller from navigation stack.
func popForm() {
navigationController?.dismiss(animated: true)
print("popped")
}
Better if you renamed the method name to dissmissForm instead of popForm.

You need to dismiss VC2 instead of pop. in self class as below :
func stopTapped() {
self.dismiss(animated: true, completion: { _ in })
}

When you are presenting any viewcontroller then you must use dismissViewController method to remove presented view controller. popViewController is used when you hqve push any viewcontroller.

When you use present then you have to use dismiss to remove that currently presented class in stack, when you dismiss it, your just next previous class will be in top of the stack. Thats all.. hope, it may helps you.

for pop a UIViewController you nee to push and not present. If you need to present a UIViewVontroller then on click on "X" you need to dismiss that viewController.
For push view controller
func addTapped() {
self.navigationController?.pushViewController(from, animated: true)
}
func stopTapped() {
self.navigationController?.popViewController(animated: true)
}
For presenting a view controller
func addTapped() {
self.present(from, animated: true, completion: nil)
}
func stopTapped() {
self.dismiss(animated: true, completion: nil)
}
You don't need to code for any protocol to push or present a UIViewController

Related

Swift dismiss current Viewcontroller after presenting new ViewController

My scenario, I am trying to create multiple present ViewController. Here, presenting new ViewController after I need to dismiss previous ViewController.
ViewController A (RootViewController) next button click to presenting ViewController B then View Controller B next button click to present ViewController C. Now, If I close ViewController C need to show ViewController A.
Here is how you can proceed,
class VCA: UIViewController {
#IBAction func onTapNextButton(_ sender: UIButton) {
if let controller = self.storyboard?.instantiateViewController(withIdentifier: "VCB") as? VCB {
self.present(controller, animated: true, completion: nil)
}
}
}
Since VCC is embedded in a UINavigationController, you need to present UINavigationController instead of VCC.
For this subclass UINavigationController and set it as class of UINavigationController in the storyboard.
class VCB: UIViewController {
#IBAction func onTapNextButton(_ sender: UIButton) {
if let controller = self.storyboard?.instantiateViewController(withIdentifier: "NavVC") {
self.dismiss(animated: false, completion: nil)
self.presentingViewController?.present(controller, animated: true, completion: nil)
}
}
}
class NavVC: UINavigationController {}
class VCC: UIViewController {
#IBAction func onTapCloseButton(_ sender: UIButton) {
self.navigationController?.dismiss(animated: true, completion: nil)
}
}

Show second modal view controller after first was closed

I have two view controller that opens modally. When the first VC closed then the second should be opened. But when I close the first one, the second one is not displayed at all.
what is the problem?
My code is:
self.dismiss(animated: true) {
let flowVC = LanguageFlowViewController()
self.present(flowVC, animated: true)
}
You need reference of view controller from where you to present first view controller.
For example, you have view controller name as X, from there your first view controller A present. So you need reference of X to present B, because A will not be available in memory.
So when you try to present second view controller using self, it will do nothing.
So, for solution assign reference of X view controller to A. In class A, declare:
var refX: X?
While present A from X, set self to refX. Like:
var aVC = A() // This is temp, you need to instantiate your view controller here.
aVC.refX = self
self.present(aVC, animated: true, completion: nil)
Now, inside view controller A, when dismiss:
var bVC = B() // This is temp, you need to instantiate your view controller here.
self.dismiss(animated: true) {
if self.refX != nil {
self.refX?.present(bVC, animated: true, completion: nil)
}
}
I hope this will help you.
If you want to open modally ThirdViewController after Dismiss SecondViewController then you have to create protocol for it.
Like we have three UIViewController(FirstViewController,SecondViewController and
ThirdViewController)
Step 1: We need to create a protocol in SecondViewController as given below code.
protocol DismissedViewProtocal {
func dismissView()
}
class SecondViewController: UIViewController {
var delegate: DismissedViewProtocal?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func dismissSecondViewAction(_sender : AnyObject) {
dismiss(animated: true) {
self.delegate?.dismissView()
}
}
Step 2: You need to add protocol in FirstViewController as given below
class FirstViewController: UIViewController, DismissedViewProtocal {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func anAction(_sender : AnyObject){
let flowVC = self.storyboard?.instantiateViewController(withIdentifier:"SecondViewController") as? SecondViewController
secondVC?.delegate = self
self.present(secondVC!, animated: true) {
}
}
func dismissView() {
let thirdVC = self.storyboard?.instantiateViewController(withIdentifier:"ThirdViewController")
self.present(thirdVC!, animated: true) {
}
}
}
Your are dismissing the current view controller by calling self.dismiss().
Therefore it is impossible for it to present anything anymore, since it is removed from the view hierarchy. As others have mentioned, try using the self.presentingViewController or self.navigationController (if it is on a navigationController) to present your new view.
However, if you need maximum flexibility create a delegate protocol. Create a protocol with a function presentForChild(viewController: UIViewController). Before your previous view presents the view from which the code in your question belongs to, give it a reference of the protocol.
Example:
protocol ChildPresentDelegate: class {
func presentForChild(vc: UIViewController)
}
class FirstController: UIViewController, ChildPresentDelegate {
func presentForChild(vc: UIViewController) {
present(vc, animated: true, completion: nil)
}
/**
other code
*/
func showControllerAsWasShownInTheQuestion() {
let controller = SecondController()
controller.delegate = self
present(controller, animated: true, completion: nil)
}
}
class SecondController: UIViewController {
weak var delegate: ChildPresentDelegate?
func dismissMySelf() {
self.dismiss(animated: true) {
delegate?.presentForChild(vc: LanguageFlowViewController())
}
}
}

How to present another view controller after dismiss from navigation controller in swift?

How to present another view controller after dismiss from navigation controller in swift ?
I am using Navigation controller.
ViewController1.swift
func pushTo(viewController: UIViewController) {
let showLocalPwd = self.storyboard?.instantiateViewController(withIdentifier: "LocalPwdVC") as! LocalPwdVC
self.navigationController?.present(showLocalPwd, animated: true, completion: nil)
}
ViewController2.swift
#IBAction func btnVerify(_ sender: Any)
{
self.dismiss(animated: true, completion: {
let vc = self.storyboard.instantiateViewController(withIdentifier: "DataVC") as! DataVC
self.navigationController.pushViewController(vc, animated: true)
})
}
After dismissing the View Controller, it will not goes to next viewcontroller i.e. DataVC
If you want to present the view controller then create a protocol in your dismissViewController
protocol dismissViewController {
func presentCompletedViewController()
}
// Then create a delegate
var delegate = dismissViewController? = nil
// If you are using action to dismiss your ViewController then call delegate
#IBAction func YourButton(_ sender: Any) {
self.dismiss(animated: true) {
self.delegate!.presentCompletedViewController()
}
}
//And then call this in your main or homeViewController
class HomeViewController: UIViewController, dismissViewController {
func presentCompletedViewController(){
// enter code to present view controller
}
// And at last don't forget to call delegate
yourVc.delegate = self
you need to call this delegate in which you are presenting your dismissViewController

Dismiss second ViewController from the third ViewController

I am trying to dismiss VC b from VC c where VC c is a popover and has a button for sign out but it is not working.
The structure of the flow is
VC a ----presents modally----> VC b ----presents a popover----> VC c
When the button in the popover is clicked the VC c and VC b must be dismissed so that (VC a)ViewWillAppear is called.
Try this:
You can dismiss your presentingViewController from child view controller as follow
self.presentingViewController?.dismiss(animated: true, completion: nil)
When you add a ViewController as childViewController
self.parent?.dismiss(animated: true, completion: nil)
If this view controller is a child of a containing view controller (e.g. a navigation controller or tab bar
controller,)
weak open var parent: UIViewController? { get }
The view controller that was presented by this view controller or its nearest ancestor.
open var presentedViewController: UIViewController? { get }
The view controller that presented this view controller (or its farthest ancestor.)
open var presentingViewController: UIViewController? { get }
If ViewControllers have hierarchy like
VC a ----presents as self.present(objects, animated: true, completion: nil) modally----> VC b ---- presents as self.present(objects, animated: true, completion: nil) popover----> VC c
And there are a button on VC c to move back to VC a then you can use:
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
First of all try to dismiss VC b from itself, not presenting the VC c just to check if it works, using: self.dismiss(animated: true, completion: nil) or if VC b is embedded in a navigation controller, like this: self.navigationController?.dismiss(animated: true, completion: nil)
If the one from above works, I would suggest you implement the delegation protocol, where VC c will delegate to VC b the dismissal, whenever it should be done. You could also use a completion block for that, containing the dismiss code.
Hope this works
// Call inside View controller C
self.presentingViewController?.dismissViewControllerAnimated(false, completion: nil)
self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
`protocol ModalHandler {
func modalDismissed()
Class SecondViewController: UIViewController, ModalHandler {
func modalDismissed() {
self.dismiss(animated: false, completion: nil)
}
func open3rdController() {
let thirdVC = ThirdViewController(_ )
thirdVC.delegate = self
self.present(thirdVC, animated: true, completion: nil)
}
class ThirdViewController: UIViewController {
func dismiss() {
self.delegate.modalDismissed()
}
}
`
Whats about a Presenter or a Coordinator.
This instance will initialize all these VCs and also present them.
From there you can also dismiss them.

How to dismiss ViewController in Swift?

I am trying to dismiss a ViewController in swift by calling dismissViewController in an IBAction
#IBAction func cancel(sender: AnyObject) {
self.dismissViewControllerAnimated(false, completion: nil)
println("cancel")
}
#IBAction func done(sender: AnyObject) {
self.dismissViewControllerAnimated(false, completion: nil)
println("done")
}
I could see the println message in console output but ViewController never gets dismissed. What could be the problem?
From you image it seems like you presented the ViewController using push
The dismissViewControllerAnimated is used to close ViewControllers that presented using modal
Swift 2
navigationController.popViewControllerAnimated(true)
Swift 4
navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
I have a solution for your problem. Please try this code to dismiss the view controller if you present the view using modal:
Swift 3:
self.dismiss(animated: true, completion: nil)
OR
If you present the view using "push" segue
self.navigationController?.popViewController(animated: true)
if you do this i guess you might not get println message in console,
#IBAction func cancel(sender: AnyObject) {
if(self.presentingViewController){
self.dismissViewControllerAnimated(false, completion: nil)
println("cancel")
}
}
#IBAction func done(sender: AnyObject) {
if(self.presentingViewController){
self.dismissViewControllerAnimated(false, completion: nil)
println("done")
}
}
In Swift 3.0 to 4.0 it's as easy as typing this into your function:
self.dismiss(animated: true, completion: nil)
Or if you're in a navigation controller you can "pop" it:
self.navigationController?.popViewController(animated: true)
embed the View you want to dismiss in a NavigationController
add a BarButton with "Done" as Identifier
invoke the Assistant Editor with the Done button selected
create an IBAction for this button
add this line into the brackets:
self.dismissViewControllerAnimated(true, completion: nil)
Use:
self.dismiss(animated: true, completion: nil)
instead of:
self.navigationController.dismissViewControllerAnimated(true, completion: nil)
From Apple documentations:
The presenting view controller is responsible for dismissing the view controller it presented
Thus, it is a bad practise to just invoke the dismiss method from it self.
What you should do if you're presenting it modal is:
presentingViewController?.dismiss(animated: true, completion: nil)
If you presenting a controller without a Navigation Controller, you can call the following code from a method of the presented controller.
self.presentingViewController?.dismiss(animated: true, completion: nil)
If your ViewController is presented modally, optional presentingViewController will be not nil and the code will be executed.
Based on my experience, I add a method to dismiss me as extension to UIViewController:
extension UIViewController {
func dismissMe(animated: Bool, completion: (()->())?) {
var count = 0
if let c = self.navigationController?.viewControllers.count {
count = c
}
if count > 1 {
self.navigationController?.popViewController(animated: animated)
if let handler = completion {
handler()
}
} else {
dismiss(animated: animated, completion: completion)
}
}
}
Then I call this method to dismiss view controller in any UIViewController subclass. For example, in cancel action:
class MyViewController: UIViewController {
...
#IBAction func cancel(sender: AnyObject) {
dismissMe(animated: true, completion: nil)
}
...
}
Since you used push presented viewController, therefore, you can use
self.dismiss(animated: false, completion: nil)
Don't create any segue from Cancel or Done to other VC and only write this code your buttons #IBAction
#IBAction func cancel(sender: AnyObject) {
dismiss(animated: false, completion: nil)
}
So if you wanna dismiss your Viewcontroller use this. This code is written in button action to dismiss VC
#IBAction func cancel(sender: AnyObject) {
dismiss(animated: true, completion: nil)
}
Here is the one way to dismiss present view controller and move back to previous view controller. You can do this through Storyboard only.
Open Storyboard
Right click on Cancel button and drag it to previous view controller, where you want to move back to previous controller
Now release the right click and you can see some actions which performs on cancel button
Now choose "popover present" option from list
Now you can dismiss your current view by click on cancel button
Please try this, It's working with me.
Second Way - Use - navigationController.popViewControllerAnimated(true)
Best luck..
For reference, be aware that you might be dismissing the wrong view controller. For example, if you have an alert box or modal showing on top of another modal. (You could have a Twitter post alert showing on top of your current modal alert, for example). In this case, you need to call dismiss twice, or use an unwind segue.
Try this:
#IBAction func close() {
dismiss(animated: true, completion: nil)
}
If you using the present method in the parent VC then you should call this function, to dismiss the child VC use this
self.dismiss(animated: true, completion: nil)
if you calling child VC by using push method, to dismiss the child VC use this
self.navigationController?.popViewController(animated: true)
If you are presenting a ViewController modally, and want to go back to the root ViewController, take care to dismiss this modally presented ViewController before you go back to the root ViewController otherwise this ViewController will not be removed from Memory and cause Memory leaks.
In Swift 3.0
If you want to dismiss a presented view controller
self.dismiss(animated: true, completion: nil)
In Swift 4.1 and Xcode 9.4.1
If you use pushViewController to present new view controller, use this
self.navigationController?.popViewController(animated: false)
#IBAction func back(_ sender: Any) {
self.dismiss(animated: false, completion: nil)
}

Resources