Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 616 Vote(s) - 3.42 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Passing data between view controllers

#41
I find simplest and most elegant version with passing blocks.
Let's name view controller that waits for returned data as "A" and returning view controller as "B". In this example we want to get 2 values: first of Type1 and second of Type2.

Assuming we use Storyboard, first controller sets callback block, for example during segue preparation:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController isKindOfClass:[BViewController class]])
{
BViewController *viewController = segue.destinationViewController;

viewController.callback = ^(Type1 *value1, Type2 *value2) {
// optionally, close B
//[self.navigationController popViewControllerAnimated:YES];

// let's do some action after with returned values
action1(value1);
action2(value2);
};

}
}

and "B" view controller should declare callback property, BViewController.h:

// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);


Than in implementation file BViewController.m after we have desired values to return our callback should be called:

if (self.callback)
self.callback(value1, value2);


One thing to remember is that using block often needs to manage strong and __weak references like explained [here][1]


[1]:

[To see links please register here]

Reply

#42
### Pure `Combine` solution for UIKit and AppKit

Let's take a simple example of passing an `Int` value of `count` between ViewControllers.

A *parent* ViewController (VC) has a variable named count, and *child* ViewController can let the user change the value of count. Once the user is done changing the value, they will dismiss the child controller and parent VC should have the updated value after that.

----
## Parent View Controller

*ParentVC* gets the *updated* value of count from the ChildVC

```
class ParentVC: UIViewController {
var count = 1
var countObserver: AnyCancellable! // 1

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let childVC = segue.destination as! ChildVC
childVC.sliderValue = count // 2
countObserver = childVC.$sliderValue // 3
.assign(to: \.count, on: self)
}
}
```

1. `countObserver` will retain the observer that would observe changes made by ChildVC

2. When the child appears, we assign the current count value from parent to a control (here UISlider) in the ChildVC, which becomes a starting point for modifying the value of `count`.

3. We observe `sliderValue` (which is a publisher) that would emit the value of count that the user would change by dragging the slider. Note that we used `$sliderValue` instead of just sliderValue.

----

## Child View Controller

ChildVC is the one that would `emit` values that ParentVC is interested in:

```
class ChildVC: UIViewController {
@Published var sliderValue: Int = 0 // 1

@IBOutlet var sliderOutlet: UISlider!

@IBAction func slided(_ sender: UISlider) {
sliderValue = Int(sender.value)
}
}
```

1. [@Published][1] is Publisher that stores value and emits a signal when the value changes. It behaves just like a regular var but can publish values that can be accessed by prepending `$` sign to it.

----

## CurrentValueSubject and PassthroughSubject vs @Published

1. [CurrentValueSubject][2] can also be used instead of @Published. The only difference is, you would have to manually emit the signal. It is useful in cases where you want to control when to emit. For example, you can emit the value, but only if it falls in a specific range.

2. [PassthroughSubject][3] also can be used instead of @Published or CurrentValueSubject. Here, the difference is PassthroughSubject cannot hold a value, it can just emit a signal. This can be useful when the value cannot be concretely represented in a variable.

[1]:

[To see links please register here]

[2]:

[To see links please register here]

[3]:

[To see links please register here]





Reply

#43
There are several ways to pass data between view controllers.

1. Delegate protocol (Backward direction).
2. NSNotification center (Both Direction).
3. UserDefault (Both Direction).
4. Direct Property (Forward Direction).
5. Closure (Backward Direction).
6. Segue (Forward Direction).
Reply

#44
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][1] in the _View Controller Programming Guide_
2. [Delegate Pattern][2]

**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][3]

[1]:

[To see links please register here]

[2]:

[To see links please register here]

[3]:

[To see links please register here]


Reply

#45
> **Swift 5**
>
> Well [Matt Price's answer][1] is perfectly fine for passing data, but I
> am going to rewrite it, in the *latest* Swift version because I believe new
> programmers find it quit challenging due to new syntax and
> methods/frameworks, as original post is in Objective-C.

There are multiple options for *passing data* between view controllers.

1. Using Navigation Controller Push
2. Using Segue
3. Using Delegate
4. Using Notification Observer
5. Using Block

I am going to rewrite his logic in Swift with the latest iOS framework

----------------

> **Passing Data through Navigation Controller Push**: *From ViewControllerA to ViewControllerB*

**Step 1.** Declare variable in ViewControllerB

var isSomethingEnabled = false

**Step 2.** Print Variable in ViewControllerB' ViewDidLoad method

override func viewDidLoad() {
super.viewDidLoad()
// Print value received through segue, navigation push
print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
}

**Step 3.** In ViewControllerA Pass Data while pushing through Navigation Controller


if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.isSomethingEnabled = true
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}


So here is the complete code for:

**ViewControllerA**

import UIKit

class ViewControllerA: UIViewController {

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

// MARK: Passing data through navigation PushViewController
@IBAction func goToViewControllerB(_ sender: Any) {

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.isSomethingEnabled = true
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
}
}

**ViewControllerB**

import UIKit

class ViewControllerB: UIViewController {

// MARK: - Variable for Passing Data through Navigation push
var isSomethingEnabled = false

override func viewDidLoad() {
super.viewDidLoad()
// Print value received through navigation push
print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
}
}

-----------------------

> **Passing Data through Segue**: *From ViewControllerA to ViewControllerB*

**Step 1.** Create Segue from ViewControllerA to ViewControllerB and give Identifier = showDetailSegue in Storyboard as shown below

[![enter image description here][2]][2]

**Step 2.** In ViewControllerB Declare a viable named **isSomethingEnabled** and print its value.

**Step 3.** In ViewControllerA pass isSomethingEnabled's value while passing Segue

So *here* is the complete code for:

**ViewControllerA**

import UIKit

class ViewControllerA: UIViewController {

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

// MARK: - - Passing Data through Segue - -
@IBAction func goToViewControllerBUsingSegue(_ sender: Any) {
performSegue(withIdentifier: "showDetailSegue", sender: nil)
}

// Segue Delegate Method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showDetailSegue") {
let controller = segue.destination as? ViewControllerB
controller?.isSomethingEnabled = true//passing data
}
}
}

**ViewControllerB**

import UIKit

class ViewControllerB: UIViewController {
var isSomethingEnabled = false

override func viewDidLoad() {
super.viewDidLoad()
// Print value received through segue
print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
}
}

-----------------------------

> **Passing Data through Delegate**: *From ViewControllerB to ViewControllerA*

**Step 1.** Declare Protocol ***ViewControllerBDelegate*** in the ViewControllerB file, but outside the class

protocol ViewControllerBDelegate: NSObjectProtocol {

// Classes that adopt this protocol MUST define
// this method -- and hopefully do something in
// that definition.
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

**Step 2.** Declare Delegate variable instance in ViewControllerB

var delegate: ViewControllerBDelegate?

**Step 3.** Send data for delegate inside viewDidLoad method of ViewControllerB

delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")

**Step 4.** Confirm ViewControllerBDelegate in ViewControllerA

class ViewControllerA: UIViewController, ViewControllerBDelegate {
// to do
}

**Step 5.** Confirm that you will implement a delegate in ViewControllerA

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.delegate = self//confirming delegate
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}

**Step 6.** Implement delegate method for receiving data in ViewControllerA

func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
print("Value from ViewControllerB's Delegate", item!)
}

So *here* is the complete code for:

**ViewControllerA**

import UIKit

class ViewControllerA: UIViewController, ViewControllerBDelegate {

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

// Delegate method
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
print("Value from ViewControllerB's Delegate", item!)
}

@IBAction func goToViewControllerForDelegate(_ sender: Any) {

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.delegate = self
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
}
}

**ViewControllerB**

import UIKit

//Protocol decleare
protocol ViewControllerBDelegate: NSObjectProtocol {
// Classes that adopt this protocol MUST define
// this method -- and hopefully do something in
// that definition.
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

class ViewControllerB: UIViewController {
var delegate: ViewControllerBDelegate?

override func viewDidLoad() {
super.viewDidLoad()
// MARK: - - - - Set Data for Passing Data through Delegate - - - - - -
delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
}
}

-----------------------

> **Passing Data through Notification Observer**: *From ViewControllerB to ViewControllerA*

Step 1. Set and post data in the notification observer in ViewControllerB

let objToBeSent = "Test Message from Notification"
NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)

Step 2. Add Notification Observer in ViewControllerA

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Step 3. Receive Notification data value in ViewControllerA

@objc func methodOfReceivedNotification(notification: Notification) {
print("Value of notification: ", notification.object ?? "")
}


So here is the complete code for:

**ViewControllerA**

import UIKit

class ViewControllerA: UIViewController{

override func viewDidLoad() {
super.viewDidLoad()

// Add observer in controller(s) where you want to receive data
NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
}

// MARK: Method for receiving Data through Post Notification
@objc func methodOfReceivedNotification(notification: Notification) {
print("Value of notification: ", notification.object ?? "")
}
}


**ViewControllerB**

import UIKit

class ViewControllerB: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

// MARK:Set data for Passing Data through Post Notification
let objToBeSent = "Test Message from Notification"
NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
}
}


--------------------------


> **Passing Data through Block**: *From ViewControllerB to ViewControllerA*

Step 1. Declare block in ViewControllerB

```
var authorizationCompletionBlock:((Bool)->())? = {_ in}
```

Step 2. Set data in block in ViewControllerB

if authorizationCompletionBlock != nil
{
authorizationCompletionBlock!(true)
}

Step 3. Receive block data in ViewControllerA

// Receiver Block
controller!.authorizationCompletionBlock = { isGranted in
print("Data received from Block is: ", isGranted)
}

So *here* is the complete code for:

**ViewControllerA**

import UIKit

class ViewControllerA: UIViewController {

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

// MARK:Method for receiving Data through Block
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showDetailSegue") {
let controller = segue.destination as? ViewControllerB
controller?.isSomethingEnabled = true

// Receiver Block
controller!.authorizationCompletionBlock = { isGranted in
print("Data received from Block is: ", isGranted)
}
}
}
}

**ViewControllerB**

import UIKit

class ViewControllerB: UIViewController {

// MARK: Variable for Passing Data through Block
var authorizationCompletionBlock:((Bool)->())? = {_ in}

override func viewDidLoad() {
super.viewDidLoad()

// MARK: Set data for Passing Data through Block
if authorizationCompletionBlock != nil
{
authorizationCompletionBlock!(true)
}
}
}

[You can find complete sample Application at my GitHub][3] Please let me know if you have any question(s) on this.

[1]:

[To see links please register here]

[2]:

[3]:

[To see links please register here]


Reply



Forum Jump:


Users browsing this thread:
2 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through