How can I pass data to another controller when starting the ViewController?
I want to transfer my data from one ViewController
VC to another VC. Is it possible?
I tried the following method and had no success:
Click the button:
self.dismiss(animated: true) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "EditViewController") as! EditViewController
controller.segueArray = [values]
}
when it EditViewController
appears again, mine segueArray
is nil
there.
How do I transfer my data from my ViewController
to EditViewController
when leaving?
source to share
The best way to pass data back to the previous view controller is through delegates ... when moving from ViewController A to B, pass the view controller A as a delegate, and in the viewWillDisappear method for ViewController B, call the delegate method in ViewController A. Protocols help define the delegate and required methods to be implemented by the previous VC. Here's a quick example:
Data transfer protocol:
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)
}
}
Leaving view controller:
class viewControllerB: UIViewController {
var delegate: isAbleToReceiveData
viewWillDisappear {
delegate.pass(data: "someData") //call the func in the previous vc
}
}
source to share
In the blocking completion block, you create a new instance of EditViewController. I am assuming another instance of EditViewController exists on the navigation stack, you need to find that instance and set the segueArray values. This can be achieved by iterating through your navigation stack view controllers, for example:
viewController.navigationController?.viewControllers.forEach({ (vc) in
if let editVC = vc as? EditViewController {
editVC.segueArray = ....
}
})
But I would recommend using the delegate pattern like:
protocol EditViewControllerDelegate: class {
func setSegueArray(segues: [UIStoryboardSegue])
}
In the viewcontroller (just call it ViewController) where the reject block is located, declare the delegate property:
class ViewController: UIViewController {
weak var delegate: EditViewControllerDelegate?
....
}
Then, after presenting an instance (let's say from an EditViewController), the ViewController sets the delegate like this:
...
if let vc = presentingViewController as? ViewController {
vc.delegate = self
}
And map the EditViewController to a delegate protocol like:
extension EditViewController: EditViewControllerDelegate {
func setSegueArray(segues: [UIStoryboardSegue]) {
// Do the data setting here eg. self.segues = segues
}
}
source to share
To detect when the back button is clicked on the view controller, I simply 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
source to share
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
}
source to share