How to pass data to another controller on dismiss ViewController?
Asked Answered
T

5

11

I want to pass my data from one ViewController to another VC on VC dismiss. Is it possible?

I've tried the next method and no success:

On button click:

self.dismiss(animated: true) { 
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let controller = storyboard.instantiateViewController(withIdentifier: "EditViewController") as! EditViewController
        controller.segueArray = [values]
    }

when EditViewController appears again, my segueArray is nil there.

How can I pass my data from my ViewController to the EditViewController on dismiss?

Truditrudie answered 30/4, 2017 at 13:0 Comment(0)
H
26

The best way to pass data back to the previous view controller is through delegates... when going from ViewController A to B, pass view controller A as a delegate and on the viewWillDisappear method for ViewController B, call the delegate method in ViewController A.. Protocols would help define the delegate and the required methods to be implemented by previous VC. Here's a quick example:

Protocol for passing data:

protocol isAbleToReceiveData {
  func pass(data: String)  //data: string is an example parameter
} 

Viewcontroller A:

class viewControllerA: UIViewController, isAbleToReceiveData {

  func pass(data: String) { //conforms to protocol
  // implement your own implementation
   }

  prepare(for: Segue) {
    /** code for passing data **/
    let vc2 = ViewCOntrollerB()  /
    vc2.delegate = self   //sets the delegate in the new viewcontroller 
                          //before displaying
    present(vc2)
  }
}

Dismissing viewcontroller:

class viewControllerB: UIViewController {

  var delegate: isAbleToReceiveData

  viewWillDisappear {
      delegate.pass(data: "someData") //call the func in the previous vc
  }
}
Hindsight answered 30/4, 2017 at 13:14 Comment(4)
var delegate: isAbleToPassData this code throwing error . it should be var delegate: isAbleToPassData!Sightless
i'd suggest making it a optional value instead var delegate: isAbleToPassData? as it would reduce chances for crashHindsight
and call the func using delegate?.pass(data: "SomeData")Hindsight
where does the protocol get written out in ViewController A or ViewController B?Neurilemma
A
1

In the dismiss completion block, you create a new instance of the EditViewController. I assume that another EditViewController instance exists back in the navigation stack, you need to find that instance & set the segueArray to values. That you can achieve by iterating through your navigation stack's viewcontrollers like:

viewController.navigationController?.viewControllers.forEach({ (vc) in
    if let editVC = vc as? EditViewController {
        editVC.segueArray = ....
    }
})

But I would recommend to use the delegate pattern, like:

protocol EditViewControllerDelegate: class {
    func setSegueArray(segues: [UIStoryboardSegue])
}

In the viewcontroller (call it just ViewController) where the dismiss block is, declare a delegate property:

class ViewController: UIViewController {
    weak var delegate: EditViewControllerDelegate?
    ....
}

Then on presenting the instance of (I assume from EditViewController) ViewController set the delegate like:

...
if let vc = presentingViewController as? ViewController {
    vc.delegate = self
}

And conform the EditViewController to the delegate protocol like:

extension EditViewController: EditViewControllerDelegate {
    func setSegueArray(segues: [UIStoryboardSegue]) {
        // Do the data setting here eg. self.segues = segues
    }
}
Abjuration answered 30/4, 2017 at 13:21 Comment(0)
R
0

To detect when the back button is pressed on a view controller, I just use:

override func didMove(toParentViewController parent: UIViewController?) {
    guard parent == nil else { return } // Back button pressed

    ... // Pass on the info as shown in you example
} // didMoveToParentViewController
Royall answered 30/4, 2017 at 13:52 Comment(0)
T
0

A generic solution: (🔸 Swift 5.1 )

/**
 * Returns a ViewController of a class Kind
 * ## Examples:
 * UIView.vc(vcKind: CustomViewController.self) // ref to an instance of CustomViewController
 */
public static func vc<T: UIViewController>(vcKind: T.Type? = nil) -> T? {
   guard let appDelegate = UIApplication.shared.delegate, let window = appDelegate.window else { return nil }
   if let vc = window?.rootViewController as? T {
      return vc
   } else if let vc = window?.rootViewController?.presentedViewController as? T {
      return vc
   } else if let vc = window?.rootViewController?.children {
      return vc.lazy.compactMap { $0 as? T }.first
   }
   return nil
}
Taxiway answered 19/8, 2019 at 11:2 Comment(1)
This answer is not clear , it better to give real example on how to use it , like how @Hindsight did . Asume you have vc1 and vc2 you want to pass data from vc2 to vc1 when vc2 is dismiss . How to use your code to do that ?Blinders
T
0

I think the easiest solution would be to use a completion handler:

Example:

class EditViewController {
    let secondController = SecondController()
    
    func showSecondController() {
        secondController.show() { data in
            //
        }
    }
}

class SecondController {
    var completion: ((_ data: Any?) -> Void)?
    
    func show(completion: @escaping (_ data: Any?) -> Void) {
        // Show something
        self.completion = completion
    }

    func dismiss() {
        if let completion = self.completion {
            completion(nil)
        }
    }
}
Trophoblast answered 28/6, 2023 at 9:13 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.