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 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)
}
Passing Data Between View Controllers in Swift 4
Your problem is that your prepare
methods never run because you never call them.
Look, when you call performSegue
, then prepare(for segue: sender:)
is called too, so you can override this method in your ViewController and because you're passing identifier
as parameter of performSegue
method you can determine what should happen if segue has this or this identifier
So, delete prepare for segue methods from IBActions
@IBAction func backButton(_ sender: Any) {
if installer == "verified"{
performSegue(withIdentifier: "main/login", sender: self)
}
}
@IBAction func button1(_ sender: Any) {
if installer == "verified"{
performSegue(withIdentifier: "Button1", sender: self)
}
}
instead override prepare(for segue: sender:)
method of ViewController and inside specify what should happen if segue has "main/login"
indentifier or "Button1"
:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "main/login" {
let mainController = segue.destination as! ViewController
mainController.myvar = installer
} else if segue.identifier == "Button1"
let IPController = segue.destination as! IP_ModuleQuickStartViewController
IPController.verified = installer
}
}
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
How to pass data between view controllers - not working
This is an inefficient way to do this as you are passing data through 3 different objects. However, going on with this methodology, the problem is the label's are not created yet inside
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let VC = segue.destination as? ThirdViewController {
VC.player1ScoreLabel.text = String(player1CurrentScore)
VC.player2ScoreLabel.text = String(player2CurrentScore)
VC.player3ScoreLabel.text = String(player3CurrentScore)
VC.player4ScoreLabel.text = String(player4CurrentScore)
}
}
See, the label isn't created yet. So, you are setting text
on aUILabel
that isn't initialized. Therefore, you need to create variables for the labels inside ThirdViewController
.
Third View Controller
class ThirdViewController: UIViewController {
@IBOutlet var player1ScoreLabel: UILabel!
@IBOutlet var player2ScoreLabel: UILabel!
@IBOutlet var player3ScoreLabel: UILabel!
@IBOutlet var player4ScoreLabel: UILabel!
var score0:Int!
var score1:Int!
var score2:Int!
var score3:Int!
override func viewDidLoad() {
super.viewDidLoad()
self.player1ScoreLabel.text = String(score0)
self.player2ScoreLabel.text = String(score1)
self.player3ScoreLabel.text = String(score2)
self.player4ScoreLabel.text = String(score3)
}
}
and change the segue in SecondViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let VC = segue.destination as? ThirdViewController {
VC.score0 = player1CurrentScore
VC.score1 = player2CurrentScore
VC.score2 = player3CurrentScore
VC.score3 = player4CurrentScore
}
}
Another Way:
Let's create a singleton called Game
. Within this (assuming you only have 4 players) we can create 4 players that will never change. This allows us to create instances of players in one location and call upon them as necessary.
NOTE: A singleton can be misused EASILY.
https://cocoacasts.com/what-is-a-singleton-and-how-to-create-one-in-swift
https://cocoacasts.com/are-singletons-bad/
class Game {
static var score0:Int = 0
static var score1:Int = 0
static var score2:Int = 0
static var score2:Int = 0
}
Then, anywhere in your code you can access Game.score0, Game.score1.
CAUTION:
I would caution you to very carefully use singletons. You don't want everything with public access. You need to determine if this is good for you or not. Cheers.
Can't pass data between View Controller and Model
I think it is supposed to be var delegate: LocationGroup()
Also, I wouldn't be calling it delegate because registered delegate is a keyword in swift
https://manasaprema04.medium.com/different-ways-to-pass-data-between-viewcontrollers-views-8b7095e9b1bf
passing data between view controllers without changing views
In your case, don't pass data.
Create a shared object to act as your data model. When users fill in the fields, update the data model.
When the user moves to the second controller/view, that controller uses the data model object to show what it needs to.
class FakeVC1 {
func userInput() {
DataModel.shared.username = "Me"
}
}
class FakeVC2 {
func viewAppears() {
if let name = DataModel.shared.username {
print(name)
} else {
print("I have nothing to say")
}
}
}
class DataModel {
static let shared = DataModel()
var username: String?
}
FakeVC1().userInput()
FakeVC2().viewAppears()
Related Topics
Ios Swift - Objective C Code Migration to Swift
How to Link to Apps on the App Store
Converting String to Int With Swift
Status Bar and Navigation Bar Issue in Ios7
Swift - Get Device'S Wifi Ip Address
How to Get Current Location from User in Ios
How to Convert a Uiview to an Image
How to Change Uipickerview Height
Uitapgesturerecognizer - Make It Work on Touch Down, Not Touch Up
How to Change Button Title Alignment in Swift
How to Add Constraints Programmatically Using Swift
How to Set Cornerradius For Only Top-Left and Top-Right Corner of a Uiview
Remove HTML Tags from an Nsstring on the Iphone
Attempt to Set a Non-Property-List Object as an Nsuserdefaults
How to Use Timer (Formerly Nstimer) in Swift
How to Count Occurrences of an Element in a Swift Array
Protocol Can Only Be Used as a Generic Constraint Because It Has Self or Associatedtype Requirements