Dismiss and Present View Controller in Swift - ios

Hi I'm trying to present a viewcontroller and dismiss my current modal view but this code is not working
self.dismissViewControllerAnimated(true, completion: {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("OrderViewController")
self.presentViewController(vc!, animated: true, completion: nil)
})
vice versa is not working too on completion block of presentviewcontroller
EDIT: replaced vc! to self

You have to get the viewController which presented self (current ViewController). If that view controller is rootViewController, then you can use the code below, if not then query it based on your view controller hierarchy.
if let vc3 = self.storyboard?.instantiateViewController(withIdentifier: "vc3") as? ViewController3 {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController!.present(vc3, animated: true, completion: nil)
}

you can't do that because when the UIViewController A calls the UIViewController B and the first controller is dismissed then the two controllers are nil.
You need to have a UIViewController as a base, in this case MainViewController is the base. You need to use a protocol to call the navigation between controllers.
you can do using protocol let say for example as bellow:-
In to your viewController setting Protocol :
protocol FirstViewControllerProtocol {
func dismissViewController()
}
class FirstViewController: UIViewController {
var delegate:FirstViewControllerProtocol!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func goBack(sender: AnyObject) {
self.dismissViewControllerAnimated(true) {
self.delegate!.dismissViewController()
}
}
Now in your main view controller
class MainViewController: UIViewController, FirstViewControllerProtocol {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func goToFirstViewController(sender: AnyObject) {
let viewController = self.storyboard?.instantiateViewControllerWithIdentifier(String(FirstViewController)) as! FirstViewController
viewController.delegate = self
self.presentViewController(viewController, animated: true, completion: nil)
}
//MARK: Protocol
func dismissViewController() {
if let viewController = self.storyboard?.instantiateViewControllerWithIdentifier(String(SecondViewController)){
self.presentViewController(viewController, animated: true, completion: nil)
}
}
Code example with storyboard:

I think there is a mistake in your code where 'self' should be the presenting view controller to present 'vc', not 'vc' its self
Your code
self.dismissViewControllerAnimated(true, completion: {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("OrderViewController")
vc!.presentViewController(vc!, animated: true, completion: nil)
})
Try this
self.dismissViewControllerAnimated(true, completion: {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("OrderViewController")
self.presentViewController(vc!, animated: true, completion: nil)
})
hope this is helpful

let parent = self.parentViewController!
parent.dismissViewControllerAnimated(true, completion: {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("OrderViewController")
parent.presentViewController(vc!, animated: true, completion: nil)
})

Here's a solution for Swift3
To present the ViewController
let NotificationVC = self.storyboard?.instantiateViewController(withIdentifier: "NotificationVC") as! ExecutiveNotificationViewController
self.present(NotificationVC, animated: true, completion: nil)
To dismiss the ViewController:
self.dismiss(animated: true, completion: nil)

Related

How to reload present view controller swift?

I have two Present view controllers. The thing i want to do is when the second Present view controller is dismissed it will automatically reload the first present view controller(Table view). note: first view controller holds a table view, basically i want to reload the table view of first controller.
ViewWillAppear code:
override func viewWillAppear(_ animated: Bool) {
tableViewReloadFromCreateProductVC()
}
func tableViewReloadFromCreateProductVC () {
tableView.reloadData()
}
Calling from second view controller code:
SecondViewController.tableViewReloadFromCreateProductVC()
navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
FirstViewController calling 2nd view controller
#IBAction func CallSecondViewButton(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "YourViewControllerIdentifier") as! YourViewController
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: true, completion: nil)
}
just write the code in viewWillAppear() method of the view controller that you want to reload like this
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
//perform api call if any
yourTableView.reloadData()
}
2nd view controller
#IBAction func CloseButton(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
after dissmissing the viewWillAppear method of firstViewController will autometically called.
The First two snippets are for first view controller and the last one is for second view controller
Reloading the entire table view could sometimes be costly and it also sounds like you're making an API call as well so unless you want your table view to be reloaded and the API call made every time the view controller becomes visible whether or not you've made changes to it, you want the reloading to be done only when it's necessary.
You can try it in a few different ways:
class CreateProductVC: UITableViewController {
#IBAction func presentSecondVC() {
if let secondVC = storyboard?.instantiateViewController(identifier: "SecondVC") as? SecondViewController {
secondVC.delegate = self
present(secondVC, animated: true, completion: nil)
}
}
}
class SecondViewController: UIViewController {
weak var delegate: CreateProductVC?
#IBAction func dismissSecondVC() {
self.dismiss(animated: true) {
self.delegate?.tableView.reloadData()
}
}
}
or
class CreateProductVC: UITableViewController {
#IBAction func presentSecondVC() {
if let secondVC = storyboard?.instantiateViewController(identifier: "SecondVC") as? SecondViewController {
secondVC.isDismissed = { [weak self] in
self?.tableView.reloadData()
}
present(secondVC, animated: true, completion: nil)
}
}
}
class SecondViewController: UIViewController {
var isDismissed: (() -> Void)?
#IBAction func dismissSecondVC() {
self.dismiss(animated: true) {
self.isDismissed?()
}
}
}
or if you want more fine-grained control over what to do with the new data:
protocol ReloadVC {
func reload(_ value: String)
}
class CreateProductVC: UITableViewController, ReloadVC {
var dataSource: [String]! {
didSet {
tableView.reloadData()
}
}
#IBAction func presentSecondVC() {
if let secondVC = storyboard?.instantiateViewController(identifier: "SecondVC") as? SecondViewController {
secondVC.delegate = self
present(secondVC, animated: true, completion: nil)
}
}
func reload(_ value: String) {
dataSource.append(value)
}
}
class SecondViewController: UIViewController {
var delegate: ReloadVC?
#IBAction func dismissSecondVC() {
self.dismiss(animated: true) {
let someValue = "Some Value"
self.delegate?.reload(someValue)
}
}
}

How to dismiss previous Viewcontroller after presenting new Viewcontroller using Swift?

My Scenario, In my project I am maintaining three ViewController (Main, VC1 and VC2). In main ViewController I am maintaining UIButton, This button click to VC1 presenting model view controller. Again, VC1 I am maintain UIButton with action click to present model to VC2. After VC2 presenting I need to dismiss VC1.
// presenting ViewController
var presentingViewController: UIViewController! = self.presentingViewController
self.dismissViewControllerAnimated(false) {
// go back to MainMenuView as the eyes of the user
presentingViewController.dismissViewControllerAnimated(false, completion: nil)
}
Try this in VC2's close button action
var vc = self.navigationController?.presentingViewController
while vc?.presentingViewController != nil {
vc = vc?.presentingViewController
}
vc?.dismiss(animated: true, completion: nil)
Hope this will work:
You need to have a UIViewController as a base, in this case MainViewController is the base ViewController. You need to use a protocol to call the navigation between Controllers.
you can do using protocol:-
In to your FirstViewController setting Protocol :
protocol FirstViewControllerProtocol {
func dismissViewController()
}
class FirstViewController: UIViewController {
var delegate:FirstViewControllerProtocol!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func goBack(sender: AnyObject) {
self.dismissViewControllerAnimated(true) {
self.delegate!.dismissViewController()
}
}
Now in your MainViewController
class MainViewController: UIViewController, FirstViewControllerProtocol {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func goToFirstViewController(sender: AnyObject) {
let viewController = self.storyboard?.instantiateViewControllerWithIdentifier(String(FirstViewController)) as! FirstViewController
viewController.delegate = self
self.presentViewController(viewController, animated: true, completion: nil)
}
//MARK: Protocol for dismiss
func dismissViewController() {
if let viewController = self.storyboard?.instantiateViewControllerWithIdentifier(String(SecondViewController)){
self.presentViewController(viewController, animated: true, completion: nil)
}
}
To solve the problem statement, what you can do is present VC2 using Main instead of VC1.
We can get the reference to Main in VC1 using
self.presentingViewController
When VC2 is presented, dismiss VC1 in the completionHandler of present(_:animated:completion:) method
class Main: UIViewController {
#IBAction func onTapButton(_ sender: UIButton) {
let vc1 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "VC1")
self.present(vc1, animated: true, completion: nil)
}
}
class VC1: UIViewController {
#IBAction func onTapButton(_ sender: UIButton) {
let vc2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "VC2")
vc2.view.backgroundColor = .blue
self.dismiss(animated: false, completion: nil)
self.presentingViewController?.present(vc2, animated: true, completion: nil)
}
}
class VC2: UIViewController {
}
This approach is giving the expected output. Let me in case anything else is required.
You need to open vc2 from the mainVC - In order to do this you need a delegate method which will tell mainVC to close the current opened vc (i.e. vc1) and in the success block open vc2.
Code Snipet:-
dismiss(animated: false) {
//Open vc2
present(vc2)
}
In this case you need to call dismiss from the view controller over which your other view controllers are presented(Main in your case).
As you stated your situation in the question above Main presents VC1 and VC1 then presents VC2:
then calling Main.dismiss(animated: true, completion: nil) will dismiss VC1 and VC2 simultaneously.
If you don't have a reference to the root controller(Main), you could chain a couple of presentingViewController properties to access it; Something like this in the topmost controller(VC2):
presentingViewController?.presentingViewController?.dismiss(animated: true)
I hope this helps.
There is a present(_:animated:completion:) method which takes a completion. Inside that you can dismiss your VC1.
Your code will look something like this
#IBAction func ButtonTapped(_ sender: Any) {
present(VC2, animated: true) {
dismiss(animated: true, completion: nil)
}
}

Cannot update textfield in delegate callback method

I have implemented delegate in LoginViewController defined in RegistrationViewController. Callback function is calling but the problem is that I am not able to update the textfield of LoginViewController in delegate method.
LoginViewController.swift
import UIKit
class LoginViewController :UIViewController,RegisterViewDelegate {
#IBOutlet weak var mobileNumber: UITextField!
#IBAction func showRegistrationView(_ sender: Any) {
let controller = storyboard?.instantiateViewController(withIdentifier: "registration") as! RegistrationViewController
controller.delegate = self
present(controller, animated: false, completion: nil)
}
func onUserRegistrationCompletion(number: String) {
print(number) // output is 05010101010
DispatchQueue.main.async {
self.mobileNumber.text! = number
print(self.mobileNumber.text!) . // output is empty
}
}
}
RegistrationViewController.swift
import UIKit
class RegistrationViewController: UIViewController {
weak var delegate:RegisterViewDelegate?
#IBAction func register(_ sender: Any) {
self.delegate?.onUserRegistrationCompletion(number: "05010101010")
let controller = self.storyboard?.instantiateViewController(withIdentifier: "login")
present(controller!, animated: false, completion: nil)
}
}
protocol RegisterViewDelegate:class {
func onUserRegistrationCompletion(number:String)
}
it means you allocating the memory again on self.storyboard?.instantiateViewController(withIdentifier: "login") in Register VC, that the reason your delegate is by default goes to nil.
#IBAction func register(_ sender: Any) {
self.delegate?.onUserRegistrationCompletion(number: "05010101010")
self.dismiss(animated: true, completion:nil)
}
I suggest you to use navigationController.
and you can push to next view this way:
let controller = storyboard?.instantiateViewController(withIdentifier: "registration") as! RegistrationViewController
controller.delegate = self
self.navigationController?.pushViewController(controller, animated: true)
and when you want to go back to previous view use this code:
self.delegate?.onUserRegistrationCompletion(number: "05010101010")
self.navigationController?.popViewController(animated: true)
And your result will be:
Check THIS sample for more info.
And don't forget to Embed In your LoginViewController into navigationController from storyboard. as shown in demo project.

viewdidload called after closing dismissing child viewcontroller

I have a main Viewcontroller and a Child Viewcontroller.
I realized that when closing the Childviewcontroller like:
self.dismissViewControllerAnimated(true, completion: {
self.dismissViewControllerAnimated(true, completion: nil);
});
it will just dismiss the child viewcontroller and I can see the mainview controller. No other code is processed
if I close the child viewcontroller with a notification to the main viewcontroller :
self.dismissViewControllerAnimated(true, completion: {
NSNotificationCenter.defaultCenter().postNotificationName("refreshtextviewer_with_bookmark", object: nil);
self.dismissViewControllerAnimated(true, completion: nil);
});
then my refreshtextviewer_with_bookmark() function is called in the main viewcontroller and in parallel the standard viewDidLoad() is also called.
Is it normal behaviour, that in this case the viewDidLoad() is called after dismissing the child viewcontroller ? Is there a way to prevent this ?
Shouldn't be called after dismissing. Tested, and not called indeed.
Here's the quick example:
PARENT
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
println(__FUNCTION__)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "refreshtextviewer_with_bookmark", name: "refreshtextviewer_with_bookmark", object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
#IBAction func buttonTap(sender: AnyObject) {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("childVC") as! ChildViewController
self.presentViewController(vc, animated: true, completion: nil)
}
func refreshtextviewer_with_bookmark() {
println(__FUNCTION__)
}
}
CHILD
class ChildViewController: UIViewController {
#IBAction func exitTap(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("refreshtextviewer_with_bookmark", object: nil)
})
}
}
FYI __FUNCTION__ macro is replaced with the function name.

Swift: Not able to dismiss modally presented LoginViewController

As SplitViewController loads, I am showing a Login Screen. On successful login, I need to go back to parent view controller. Somehow dismissal is not working for me. Here is the code:
ParentViewController:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if !appDelegate.loggedIn {
self.performSegueWithIdentifier("loginScreen", sender: self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
Child ViewController:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.loggedIn = true
self.dismissViewControllerAnimated(true, completion: nil)
The dismissal part never works. It just hangs on Login Screen.
Try one of the following:
1) remove self. keep only dismissViewControllerAnimated(true, completion: nil)
or remove self. and make it:
2) presentingViewController.dismissViewControllerAnimated(true, completion: nil)
or remove self. and try:
3) presentedViewController.dismissViewControllerAnimated(true, completion: nil)
Try this in your parent view controller:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if !appDelegate.loggedIn {
let loginVC: UIViewController = self.storyboard!.instantiateViewControllerWithIdentifier("LoginViewController") as UIViewController
loginVC = UIModalTransitionStyle.CoverVertical
self.parentViewController?.presentViewController(loginVC, animated: true, completion: nil)
}
}
You're instantiating the new view controller by its own name rather than by the segue name.

Resources