Sending Data from One Viewcontroller to Another

Passing data between view controllers

This question seems to be very popular here on Stack Overflow so I thought I would try and give a better answer to help out people starting in the world of iOS like me.

Passing Data Forward

Passing data forward to a view controller from another view controller. You would use this method if you wanted to pass an object/value from one view controller to another view controller that you may be pushing on to a navigation stack.

For this example, we will have ViewControllerA and ViewControllerB

To pass a BOOL value from ViewControllerA to ViewControllerB we would do the following.

  1. in ViewControllerB.h create a property for the BOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. in ViewControllerA you need to tell it about ViewControllerB so use an

     #import "ViewControllerB.h"

Then where you want to load the view, for example, didSelectRowAtIndex or some IBAction, you need to set the property in ViewControllerB before you push it onto the navigation stack.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.isSomethingEnabled = YES;
[self pushViewController:viewControllerB animated:YES];

This will set isSomethingEnabled in ViewControllerB to BOOL value YES.

Passing Data Forward using Segues

If you are using Storyboards you are most likely using segues and will need this procedure to pass data forward. This is similar to the above but instead of passing the data before you push the view controller, you use a method called

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

So to pass a BOOL from ViewControllerA to ViewControllerB we would do the following:

  1. in ViewControllerB.h create a property for the BOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. in ViewControllerA you need to tell it about ViewControllerB, so use an

     #import "ViewControllerB.h"
  3. Create the segue from ViewControllerA to ViewControllerB on the storyboard and give it an identifier. In this example we'll call it "showDetailSegue"

  4. Next, we need to add the method to ViewControllerA that is called when any segue is performed. Because of this we need to detect which segue was called and then do something. In our example, we will check for "showDetailSegue" and if that's performed, we will pass our BOOL value to ViewControllerB

     -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"showDetailSegue"]){
    ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
    controller.isSomethingEnabled = YES;
    }
    }

If you have your views embedded in a navigation controller, you need to change the method above slightly to the following

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"showDetailSegue"]){
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
controller.isSomethingEnabled = YES;
}
}

This will set isSomethingEnabled in ViewControllerB to BOOL value YES.

Passing Data Back

To pass data back from ViewControllerB to ViewControllerA you need to use Protocols and Delegates or Blocks, the latter can be used as a loosely coupled mechanism for callbacks.

To do this we will make ViewControllerA a delegate of ViewControllerB. This allows ViewControllerB to send a message back to ViewControllerA enabling us to send data back.

For ViewControllerA to be a delegate of ViewControllerB it must conform to ViewControllerB's protocol which we have to specify. This tells ViewControllerA which methods it must implement.

  1. In ViewControllerB.h, below the #import, but above @interface you specify the protocol.

     @class ViewControllerB;

    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
  2. Next still in the ViewControllerB.h, you need to set up a delegate property and synthesize in ViewControllerB.m

     @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
  3. In ViewControllerB we call a message on the delegate when we pop the view controller.

     NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
  4. That's it for ViewControllerB. Now in ViewControllerA.h, tell ViewControllerA to import ViewControllerB and conform to its protocol.

     #import "ViewControllerB.h"

    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
  5. In ViewControllerA.m implement the following method from our protocol

     - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
    NSLog(@"This was returned from ViewControllerB %@", item);
    }
  6. Before pushing viewControllerB to navigation stack we need to tell ViewControllerB that ViewControllerA is its delegate, otherwise we will get an error.

     ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];


References

  1. Using Delegation to Communicate With Other View Controllers in the View Controller Programming Guide
  2. Delegate Pattern

NSNotification center

It's another way to pass data.

// Add an observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];

-(void) handleDeepLinking:(NSNotification *) notification {
id someObject = notification.object // Some custom object that was passed with notification fire.
}

// Post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];

Passing Data back from one class to another (A class can be any controller, Network/session manager, UIView subclass or any other class)

Blocks are anonymous functions.

This example passes data from Controller B to Controller A

Define a block

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h

Add block handler (listener)

Where you need a value (for example, you need your API response in ControllerA or you need ContorllerB data on A)

// In ContollerA.m

- (void)viewDidLoad {
[super viewDidLoad];
__unsafe_unretained typeof(self) weakSelf = self;
self.selectedVoucherBlock = ^(NSString *voucher) {
weakSelf->someLabel.text = voucher;
};
}

Go to Controller B

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
[self.navigationController pushViewController:vc animated:NO];

Fire block

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath {
NSString *voucher = vouchersArray[indexPath.row];
if (sourceVC.selectVoucherBlock) {
sourceVC.selectVoucherBlock(voucher);
}
[self.navigationController popToViewController:sourceVC animated:YES];
}

Another Working Example for Blocks

How to transfer data from one view controller to another

Remove these lines :

let vct = self.storyboard?.instantiateViewController(withIdentifier: "Home2")
self.present(vct!, animated: true, completion: nil)

You are already pushing the same viewController in navigationController.

Your final code should be :

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
var (cellName) = myList[indexPath.row]

let viewController = storyboard?.instantiateViewController(withIdentifier: "Home2") as! ShopViewController
viewController.name = cellName
navigationController?.pushViewController(viewController, animated: true)
print("row\(indexPath.row)")
print("name: \(cellName)")
}

This should work only once you have set the viewController's identifier in storyboard. This is how you can do that :

Sample Image

Passing data between a ViewController to another ViewController with navigation controller Swift 5

If you are using segue, then add "segue_identifier" in storyboard and the in secondviewcontroller add:

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue_ identifier" {
let mainTab = segue.destination as! tabBarController
let nav = mainTab.viewControllers[0] as! UINavigationController
let vc = nav.viewControllers.first as! HomeViewController
vc.cid = cityId
}
}

Because your segue destination is UINavigationController, you need to send data to view controller like this

Passing data from one view controller to another

Make string variable in your AppDelegate Like

var Model_id = String()

AppDelegate

and where you are getting value of username and userid sends its value to AppDelegate. make AppDelgate object like below and same in destinationViewController

let appDelegate = UIApplication.shared.delegate as! AppDelegate

to pass value to app delegate variable

self.appDelegate. Model_id = "your Value"

SourceView from which data is to be passed

and get your values wherever you want to fetch like below

let appDelegate = UIApplication.shared.delegate as! AppDelegate
let string = appDelegate. Model_id as! String

Destination ViewController

How to pass data from First ViewController directly to the Third View Controller using segue?

assign storybordID to your controller for ex here i have assigned "HomeVC" (it would be greate if you assign class name and storybordid same)

  let objthirdController = self.storyboard?.instantiateViewController(withIdentifier: "thirdControllerStoerybordID") as! thirdControllerclassname
objthirdController.variable = self.variable
self.navigationController?.pushViewController(objthirdController, animated: true)

Swift 3 pass data from one ViewController to another ViewController

One the simpler way to pass info from one VC to another is either through an initiliazer, or through a variable that you set before presenting the second VC.

Since you are new to this, try the variable approach for now, so if say, you're passing a string:

class LoggedInVCViewController : UIViewController {

var info : String? {
didSet {
if let newInfo = self.info {
//do what ever you need to do here
}
}
}

override viewDidLoad() {
super.viewDidLoad()

}

}

func presentLoggedInScreen(yourInfo: String) {
let stroyboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let loggedInVC:LoggedInVCViewController =
storyboard.instantiateViewController(withIdentifier: "loggedInVC") as!
LoggedInVCViewController
loggedInVC.info = yourInfo
self.present(loggedInVC, animated: true, completion: nil)
}

Mind you, you can always use any other king of variable Class for info. You also would program your VC to execute some methods inside of the get bracket of the property, populate some fields based on the content of info, load a specific UI etc.

Another usefull way is to go the initialization route, but unfortunately you cannot use it with Storyboard nibs (sad i know, but this post goes over this nicely), but it still usefull whenever you will feel comfortable enough to initialize and design VC's programmatically (which I would learn ASAP if were you).

You pass a variable in a custom intializer method for your VC:

class LoggedInVCViewController : UIViewController {

var info : Any? {
get {
if let this = self.info {
return this
} else {
return nil
}
} set {
if let new = newValue {
//
}
}
}

init(info: Any?) {
//This first line is key, it also calls viewDidLoad() internally, so don't re-write viewDidLoad() here!!
super.init(nibName: nil, bundle: nil)

if let newInfo = info {
//here we check info for whatever you pass to it
self.info = newInfo
}
}

override func viewDidLoad() {
super.viewDidLoad()
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

}

So you would use is as:

func presentLoggedInScreen(yourInfo: String) {
let loggedInVC = LoggedInVCViewController(info: yourInfo)
self.present(loggedInVC, animated: true, completion: nil)
}

Obviously, as stated, this method cannot be used with Storyboards, but very usefull and, as I'm sure you can see, is much more compact.

I suggest you get familiar with Swift Properties of the docs, along with some blog tips such as this one. You can get pretty creative after a while.

For learning more programmatic approaches, i strongly recommend this YouTube channel: Let's Build That App, I personnaly haven't found a better reference point for programmatic approach Swift programming.

Don't hesitate to ask questions!

UPDATE

Your IBAction should look like this:

@IBAction func creatAccountPressed(_ sender: Any) {
if let email = emailTextField.text, let password = passwordTextField.text, let name = nameTextField.text {
Auth.auth().createUser(withEmail: email, password: password, completion: { user, error in
if let firebaseError = error {
print(firebaseError.localizedDescription)
}

let userID = user!.uid
let userEmail: String = self.emailTextField.text!
let fullName: String = self.nameTextField.text!

self.ref.child("users").child(userID).setValue(["Email": userEmail, "Name": fullName])

self.userID1 = user!.uid as! String

print(self.userID1)

presentLoggedInScreen(yourInfo: self.userID1)

})

}
}

how to pass data from one view controller to another and between the view controller there is a reveal view controller]

You can make use of userDefaults, eg in LoginViewController save data to be used, as done below:

UserDefault.standard.setValue(name, forKey: "User_name")
UserDefault.standard.synchronize()

To use saved data on profile screen:

if let userName = UserDefault.standard.value(forKey: "User_name") as? String {
self.lblUserName.text = userName
}

Hope this helps out somehow.



Related Topics



Leave a reply



Submit