How to pass data from modal view controller back when dismissed
Depending on the data you want to pass, you can create a property in the presenting view controller, which you can set when dismissing the modal view controller, so you can spare yourself the delegate.
For example, you have a ContactsViewController
, holding a var contacts: [Contact] = []
property. When you want to create a new contact, you present a modal view controller with the different values you need to create a new Contact
object. When you are done and want to dismiss the view controller, you call the function as you did in your code, but set the property in the ContactsViewController
. It will look something like this:
@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let presenter = presentingViewController as? ContactsViewController {
presenter.contacts.append(newContact)
}
dismiss(animated: true, completion: nil)
}
If you don't want to use a delegate, this is how you go about it:
In your OOTDListViewController
:
var testValue: String = ""
@IBAction func printReceivedValue(_ sender: UIButton) {
print(testValue)
}
In your modal view controller (I'll call it PresentedViewController
) :
@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
// if your OOTDListViewController is part of a UINavigationController stack, this check will probably fail.
// you need to put a breakpoint here and check if the presentingViewController is actually a UINavigationController.
// in that case, you will need to access the viewControllers variable and find your OOTDListViewController
if let presenter = presentingViewController as? OOTDListViewController {
presenter.testValue = "Test"
}
dismiss(animated: true, completion: nil)
}
If you want to use a delegate, this is how to do it:
In your OOTDListViewController:
protocol ModalDelegate {
func changeValue(value: String)
}
class OOTDListViewController: ModalDelegate {
var testValue: String = ""
@IBAction func presentViewController() {
// here, you either create a new instance of the ViewController by initializing it, or you instantiate it using a storyboard.
// for simplicity, I'll use the first way
// in any case, you cannot use a storyboard segue directly, bevause you need access to the reference of the presentedViewController object
let presentedVC = PresentedViewController()
presentedVC.delegate = self
present(presentedVC, animated: true, completion: nil)
}
func changeValue(value: String) {
testValue = value
print(testValue)
}
}
In your PresentedViewController
:
class PresentedViewController {
var delegate: ModalDelegate?
var testValue: String = ""
@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let delegate = self.delegate {
delegate.changeValue(testValue)
}
dismiss(animated: true, completion: nil)
}
}
How to pass data to another controller on dismiss ViewController?
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
}
}
Pass data when dismiss modal viewController in swift
You didn't set a delegate so it was empty when you tried to call backFromCamera()
.
Here's a simple working example you can test out. Notice the use of the optional type (?) for the delegate.
// Camera class
protocol communicationControllerCamera {
func backFromCamera()
}
class Camera: UIViewController {
var delegate: communicationControllerCamera? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.delegate?.backFromCamera()
}
}
// SceneBuilder class
class SceneBuilder: UIViewController, communicationControllerCamera {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
var myCamera = Camera()
myCamera.delegate = self
self.presentModalViewController(myCamera, animated: true)
}
func backFromCamera() {
println("Back from camera")
}
}
You can find all the information you need in Apple's Swift documentation.
Passing data while dismissing a view
There are a few ways you can accomplish this.
You can define an unwind segue from your presented view controller back to another view controller further back (usually the VC that did the original presentation). Unwind segues can be triggered from buttons much like "regular" segues, and since they're segues, they trigger -prepareForSegue:
in the dismissing VC. You can use that just like you otherwise would to push data back. Unwind segues are documented in this tech note.
Alternately, you can set up delegation from the presented view controller to another VC. This pattern is widely used in iOS development, and involves a few steps:
- Define a protocol ("SettingsDelegate") for classes to conform to. Give it a method – something like
settingsDidChange(_:)
. Make that method take an argument with the data you want to pass back. - Give your Settings controller a weak
delegate
property of typeSettingsDelegate?
. When first presenting that VC, set the delegate to the presenter. - Make the presenting VC conform to the delegate protocol, and implement the method to update as you see fit.
- Have the Done button in the presented VC call
self.delegate.settingsDidChange(_:)
, passing the new data. The presenting VC will get this call and update as Settings dismisses.
The delegation pattern can be tricky to set up the first time, but gets easier as you go. It's documented here.
Finally, you could use a persistent data store to stash settings in. UserDefaults is a good option for settings data – it lets you keep key/value pairs of information in a way that's accessible throughout your app. Read up on user defaults here.
To update when user defaults values change, you could have your Settings controller post a Notification when dismissing. Then, other VCs in your app could listen for this notification and update as needed.
How to pass data from modal view controller back when dismissed
Depending on the data you want to pass, you can create a property in the presenting view controller, which you can set when dismissing the modal view controller, so you can spare yourself the delegate.
For example, you have a ContactsViewController
, holding a var contacts: [Contact] = []
property. When you want to create a new contact, you present a modal view controller with the different values you need to create a new Contact
object. When you are done and want to dismiss the view controller, you call the function as you did in your code, but set the property in the ContactsViewController
. It will look something like this:
@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let presenter = presentingViewController as? ContactsViewController {
presenter.contacts.append(newContact)
}
dismiss(animated: true, completion: nil)
}
If you don't want to use a delegate, this is how you go about it:
In your OOTDListViewController
:
var testValue: String = ""
@IBAction func printReceivedValue(_ sender: UIButton) {
print(testValue)
}
In your modal view controller (I'll call it PresentedViewController
) :
@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
// if your OOTDListViewController is part of a UINavigationController stack, this check will probably fail.
// you need to put a breakpoint here and check if the presentingViewController is actually a UINavigationController.
// in that case, you will need to access the viewControllers variable and find your OOTDListViewController
if let presenter = presentingViewController as? OOTDListViewController {
presenter.testValue = "Test"
}
dismiss(animated: true, completion: nil)
}
If you want to use a delegate, this is how to do it:
In your OOTDListViewController:
protocol ModalDelegate {
func changeValue(value: String)
}
class OOTDListViewController: ModalDelegate {
var testValue: String = ""
@IBAction func presentViewController() {
// here, you either create a new instance of the ViewController by initializing it, or you instantiate it using a storyboard.
// for simplicity, I'll use the first way
// in any case, you cannot use a storyboard segue directly, bevause you need access to the reference of the presentedViewController object
let presentedVC = PresentedViewController()
presentedVC.delegate = self
present(presentedVC, animated: true, completion: nil)
}
func changeValue(value: String) {
testValue = value
print(testValue)
}
}
In your PresentedViewController
:
class PresentedViewController {
var delegate: ModalDelegate?
var testValue: String = ""
@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
if let delegate = self.delegate {
delegate.changeValue(testValue)
}
dismiss(animated: true, completion: nil)
}
}
dismissModalViewController AND pass data back
You need to use delegate protocols... Here's how to do it:
Declare a protocol in your secondViewController's header file. It should look like this:
#import <UIKit/UIKit.h>
@protocol SecondDelegate <NSObject>
-(void)secondViewControllerDismissed:(NSString *)stringForFirst
@end
@interface SecondViewController : UIViewController
{
id myDelegate;
}
@property (nonatomic, assign) id<SecondDelegate> myDelegate;
Don't forget to synthesize the myDelegate in your implementation (SecondViewController.m) file:
@synthesize myDelegate;
In your FirstViewController's header file subscribe to the SecondDelegate protocol by doing this:
#import "SecondViewController.h"
@interface FirstViewController:UIViewController <SecondDelegate>
Now when you instantiate SecondViewController in FirstViewController you should do the following:
// If you're using a view controller built with Interface Builder.
SecondViewController *second = [[SecondViewController alloc] initWithNibName:"SecondViewController" bundle:[NSBundle mainBundle]];
// If you're using a view controller built programmatically.
SecondViewController *second = [SecondViewController new]; // Convenience initializer that uses alloc] init]
second.myString = @"This text is passed from firstViewController!";
second.myDelegate = self;
second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second animated:YES];
[second release];
Lastly, in the implementation file for your first view controller (FirstViewController.m) implement the SecondDelegate's method for secondViewControllerDismissed:
- (void)secondViewControllerDismissed:(NSString *)stringForFirst
{
NSString *thisIsTheDesiredString = stringForFirst; //And there you have it.....
}
Now when you're about to dismiss the second view controller you want to invoke the method implemented in the first view controller. This part is simple. All you do is, in your second view controller, add some code before the dismiss code:
if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:)])
{
[self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!"];
}
[self dismissModalViewControllerAnimated:YES];
Delegate protocols are EXTREMELY, EXTREMELY, EXTREMELY useful. It would do you good to familiarize yourself with them :)
NSNotifications are another way to do this, but as a best practice, I prefer using it when I want to communicate across multiple viewControllers or objects. Here's an answer I posted earlier if you're curious about using NSNotifications: Firing events accross multiple viewcontrollers from a thread in the appdelegate
EDIT:
If you want to pass multiple arguments, the code before dismiss looks like this:
if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:argument2:argument3:)])
{
[self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!" argument2:someObject argument3:anotherObject];
}
[self dismissModalViewControllerAnimated:YES];
This means that your SecondDelegate method implementation inside your firstViewController will now look like:
- (void) secondViewControllerDismissed:(NSString*)stringForFirst argument2:(NSObject*)inObject1 argument3:(NSObject*)inObject2
{
NSString thisIsTheDesiredString = stringForFirst;
NSObject desiredObject1 = inObject1;
//....and so on
}
Related Topics
This Action Could Not Be Completed. Try Again (-22421)
Uinavigationcontroller "Back Button" Custom Text
How to Enable Back/Left Swipe Gesture in Uinavigationcontroller After Setting Leftbarbuttonitem
"Untrusted App Developer" Message When Installing Enterprise iOS Application
How to Add Images for Different Screen Size from Assets.Xcassets in Xcode 8
Making a Button Persistent Across All View Controllers
Uitextfield Securetextentry Bullets with a Custom Font
What Should Xcode 6 Gitignore File Include
Unbalanced Calls to Begin/End Appearance Transitions for <Uitabbarcontroller: 0X197870>
iPhone - Draw Transparent Rectangle on Uiview to Reveal View Beneath
Uitableview - Scroll to the Top
How to Create a Colored 1X1 Uiimage on the iPhone Dynamically
Draw Dotted (Not Dashed!) Line, with Ibdesignable in 2017
Present and Dismiss Modal View Controller
Optional Binding with Try? and As? Still Produces an Optional Type
How to Set an Nsdate Object to Midnight
Nsmutableurlrequest Timeout Interval Not Taken into Consideration for Post Requests
How to Implement Auto-Complete for Address Using Apple Map Kit