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.
in
ViewControllerB.h
create a property for theBOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
in
ViewControllerA
you need to tell it aboutViewControllerB
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:
in
ViewControllerB.h
create a property for theBOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
in
ViewControllerA
you need to tell it aboutViewControllerB
, so use an#import "ViewControllerB.h"
Create the segue from
ViewControllerA
toViewControllerB
on the storyboard and give it an identifier. In this example we'll call it"showDetailSegue"
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 ourBOOL
value toViewControllerB
-(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.
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;
@endNext still in the
ViewControllerB.h
, you need to set up adelegate
property and synthesize inViewControllerB.m
@property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
In
ViewControllerB
we call a message on thedelegate
when we pop the view controller.NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];That's it for
ViewControllerB
. Now inViewControllerA.h
, tellViewControllerA
to importViewControllerB
and conform to its protocol.#import "ViewControllerB.h"
@interface ViewControllerA : UIViewController <ViewControllerBDelegate>In
ViewControllerA.m
implement the following method from our protocol- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(@"This was returned from ViewControllerB %@", item);
}Before pushing
viewControllerB
to navigation stack we need to tellViewControllerB
thatViewControllerA
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
- Using Delegation to Communicate With Other View Controllers in the View Controller Programming Guide
- 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
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
Swift 3 - Passing data between a View Controller and after that to another 2
Before calling presentViewController
add :
nextViewController.name = yourTextField.text
You could also delete the segue
call. That is redundant.
Here is an example that I've used in the past :
@IBAction func doSegue(_ sender: UIButton) {
buttonTag = sender.tag
let storyboard = UIStoryboard (name: "Main", bundle: nil)
let resultVC = storyboard.instantiateViewController(withIdentifier: "ResultViewController")as! ResultViewController
// Communicate with new VC - These values are stored in the destination
// you can set any value stored in the destination VC here
resultVC.firstValue = buttonTag
resultVC.secondValue = randomOpponentValue()
self.navigationController?.pushViewController(resultVC, animated: true)
}
Passing values from one view controller to another in Swift
Here's a general solution with two assumptions. First, UserId is not a UILabel. Second, you meant to use view
which was instantiated in the second line, instead of using secondViewController
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var view: Dashboard = self.storyboard?.instantiateViewControllerWithIdentifier("Dashboard") as Dashboard
view.userId = employeesId[indexPath.row]
self.navigationController?.pushViewController(view, animated: true)
}
Here's what Dashboard looks like:
class Dashboard: UIViewController {
var userId: String!
@IBOutlet var userIDLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
userIDLabel.text = UserId
}
...
}
Passing a variable result from one view controller to another non-view controller swift file
Generally from what you are describing this is simply passing your object between view controllers you are having. Assume having:
class LoginData {
var username: String?
}
Now your first view controller seems to be your entry point so it is the one that creates it for instance on "next" button pressed. Then this object should be passed to the new view controller:
@IBAction onNextPressed() {
let loginData = LoginData() // Create object
loginData.username = self.textField.text // Set data to it
let nextController = UIStoryboard(...) as! NextViewController // Generate new view controller
nextController.loginData = loginData // Assign login data
navigationController.push(... nextController ...) // Show controller
}
So then the next view controller should use this object:
class NextViewController: UIViewController {
var loginData: LoginData?
override func viewDidLoad() {
super.viewDidLoad()
self.label.text = loginData?.username
}
...
In some cases though (which seems like yours) it might make sense to preserve data globally. To do so all you need is a static variable. In your case I suggest you create a new object User
.
class User {
static var current: User?
var loginData: LoginData?
}
Now here User
only contains login data but there is also a placeholder to add an instance of a current user which may be used whenever inside the app.
Now for this case the first view controller does not need to pass the data to next view controller but rather set the current user:
@IBAction onNextPressed() {
let loginData = LoginData() // Create object
loginData.username = self.textField.text // Set data to it
let newUser = User() // Create a new user
newUser.loginData = loginData // Assign login data to it
User.current = newUser // Set current user
let nextController = UIStoryboard(...) // Generate new view controller
navigationController.push(... nextController ...) // Show controller
}
And the new view controller does not need a reference to login data:
class NextViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.label.text = User.current?.loginData?.username
}
...
This is great when you need to use these data all over the app so as you mentioned some url you can now call anywhere User.current?.serviceURL
if you implement:
extension User {
var serviceURL: URL? {
guard let username = username else {
return nil
}
return URL(string: "https://some.pat/\(username)/resource_name")
}
}
I hope this clears a few things about passing data in Swift.
Passing data from one view controller to another
Make string variable in your AppDelegate Like
var Model_id = String()
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"
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
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)
})
}
}
passing data between view controllers programmatically Swift
you need to follow some steps
step1
initially embed with your initial VC to navigation controller , for e.g
Now select the first controller in Storyboard and click on Editor > Embed in... > Navigation Controller.
step2
Now you have to link the second Controller in Storyboard with your new SecondViewController.swift file.
Select the yellow circle at the top of the controller, click on the Identify inspector panel icon on the right side of the XCode window, and type the name of your new .swift file in the Class and StoryboardID fields
for e.g
step3
Passing a String
Now select the other controller in Storyboard and add this variable right below the SecondViewController class declaration:
class SecondViewController: UIViewController {
let secondLabel = UILabel()
var stringPassed = ""
Make the app assign the value of this variable to secondLabel with the following line of code in the viewDidLoad() method
step4
on your first VC , inside the button
func buttonTarget() {
let myVC = storyboard?.instantiateViewControllerWithIdentifier("SecondVC") as! SecondViewController
myVC.stringPassed = label.text!
navigationController?.pushViewController(myVC, animated: true)
}
finally you get the out put as
func setupLabelSecond() {
secondLabel.frame = CGRect(x: 40, y: 80, width: 300, height: 60)
if let outputText = stringPassed
{
secondLabel.text = outputText
}else
{
secondLabel.text = "this is Second Page"
}
secondLabel.textColor = UIColor.yellow
secondLabel.font = UIFont.boldSystemFont(ofSize: 25)
secondLabel.textAlignment = .center
secondLabel.layer.borderWidth = 2
secondLabel.layer.borderColor = UIColor.yellow.cgColor
secondLabel.layer.cornerRadius = 5
view.addSubview(secondLabel)
}
for sample you can get the tutorial here
update for XIB
func buttonTarget() {
var vcPass = SecondViewController(nibName: "SecondViewController", bundle: nil)
vcPass.stringPassed = label.text!
self.navigationController?.pushViewController(vcPass, animated: true)
}
update for without XIB and Storboard
change your appdelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
// Override point for customization after application launch.
let navigation = UINavigationController(rootViewController: MainViewController())
self.window?.rootViewController = navigation
self.window?.makeKeyAndVisible()
return true
}
on your MainViewController
func buttonTarget() {
var vcPass = SecondViewController()
vcPass.stringPassed = label.text!
self.navigationController?.pushViewController(vcPass, animated: true)
}
Passing a String
Now select the other controller in Storyboard and add this variable right below the SecondViewController class declaration:
class SecondViewController: UIViewController {
let secondLabel = UILabel()
var stringPassed = ""
Make the app assign the value of this variable to secondLabel with the following line of code in the viewDidLoad() method
func setupLabelSecond() {
secondLabel.frame = CGRect(x: 40, y: 80, width: 300, height: 60)
if let outputText = stringPassed
{
secondLabel.text = outputText
}else
{
secondLabel.text = "this is Second Page"
}
secondLabel.textColor = UIColor.yellow
secondLabel.font = UIFont.boldSystemFont(ofSize: 25)
secondLabel.textAlignment = .center
secondLabel.layer.borderWidth = 2
secondLabel.layer.borderColor = UIColor.yellow.cgColor
secondLabel.layer.cornerRadius = 5
view.addSubview(secondLabel)
}
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)
How to pass data from one view controller to another?
You can try this Solution for Data Transfer.
In FirstViewController.swift
let str = "Hello It's Done"
func callsecondViewController()
{
let controller = storyboard?.instantiateViewController(withIdentifier: "second") as! secondViewController
self.present(controller, animated: true, completion: nil)
}
func temp() -> NSString
{
return str as NSString
}
in SecondViewController.swift
var data = [NSString]()
override func viewDidLoad()
{
super.viewDidLoad()
let view = ViewController()
data = [view.temp()]
print(data)
}
and you get the output String as Hello It's Done
Related Topics
Stop Overscroll When Using "-Webkit-Overflow-Scrolling: Touch"
CSS Flexible Box Layout on Ipad
Uilabel Word Wrap Feature Leaving Space Even When Sufficient Space Available for The Word
Responseserializer 'Cannot Call Value of Non-Function Type 'Nshttpurlresponse'' with Swift 3
Save The Exif Metadata Using The New PHPhotolibrary
Detect Collision of Two UIview's in Swift
Prepareforsegue Not Called from Custom UItableviewcell
Passing Data Between View Controllers: from UItableview to a Details View Controller
Default Uifont Size and Weight But Also Support Preferredfontfortextstyle
How to Add Action to Uialertview in Swift (iOS 7)
Translate Just 4 Lines of Code from Objective C to Swift (Pointers)
iOS 7 Weather App Expand/Collapse Transition
iOS 8 Code Working on iPhone 5S But Not iPhone 5
How to Get Nsnumberformatter Currency Style from Isocurrencycodes