theProject
theProject

Reputation: 145

How to pass values between 2 View Controllers without protocol?

I have two view controllers, call them viewA and ViewB

Now, the user touches one IBAction button, which programmatically just needs to:

  1. change the value of a BOOL, call it myBOOL to YES
  2. dismiss ViewB
  3. pass the myBOOL variables current state of YES back to ViewA

I have declared the same BOOL, set property, synthesized on both Views, but per my NSLog upon dismissal of ViewB and loading back up ViewA, it reverts back to NO

So I know I'm going off on a tangent, I just want to know if you can send the value of a BOOL between two controllers and if so, please show me an example... as searches have found Protocols and Delegate examples with NSString's, and when I attempt with a BOOL I get stuck in an import loop, however I've read that its possible to make a global BOOL, as bad design as it is, I just need to get over this block for now.

Upvotes: 0

Views: 4297

Answers (7)

Nirav Jain
Nirav Jain

Reputation: 5107

There are two options available storing and retrieving data in different view controllers.

1)NSUserDefaults is best option for storing data and accessing in any other view controllers.

The NSUserDefaults class provides convenience methods for accessing common types such as float, double, integer, Boolean.

A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.

This is very easy and best method for storing and retrieving data.

if you want to read about NSUserDefaults, here I am sharing document. [NsuserDefaults Document.][1]

2) You would create properties when you want them to be accessible outside the class or other view controllers.

Create property in this way. @property (nonatomic, retain) NSArray *arrayData; and then you can use this array value in other view controllers also.

Properties replace the accessor methods for objects.

Upvotes: 1

Nirav Jain
Nirav Jain

Reputation: 5107

There are two options available storing and retrieving data in different view controllers.

1)NSUserDefaults is best option for storing data and accessing in any other view controllers.

The NSUserDefaults class provides convenience methods for accessing common types such as float, double, integer, Boolean.

A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.

This is very easy and best method for storing and retrieving data.

if you want to read about NSUserDefaults, here I am sharing document. NsuserDefaults Document.

2) You would create properties when you want them to be accessible outside the class or other view controllers.

Create property in this way. @property (nonatomic, retain) NSArray *arrayData; and then you can use this array value in other view controllers also.

Properties replace the accessor methods for objects.

You can see my answer here. Pass value from one view controller to another

Upvotes: 1

NSCry
NSCry

Reputation: 1652

I think best way to use powerful features of blocks in below ways.

In ViewB.h

typedef void (^CompletionHandler)(BOOL myBool);
@interface ViewB : UIViewController {
    CompletionHandler completionHandler;
}
- (void)dismissHandler:(CompletionHandler)handler;

In ViewB.m

- (void)dismissHandler:(CompletionHandler)handler {
    completionHandler = handler;
}
- (IBAction)dismiss:(id)sender {
    completionHandler (YES); // your yes no logic here
}

In ViewA.m

- (IBAction)showPopup:(id)sender {
    ViewB *vc = [[ViewB alloc] init];
    [self.view addSubview:vc.view];
    [vc dismissHandler:^(BOOL myBool) {
        if (myBool) {
                //Do your work;
        }
    }];
}

Upvotes: 0

gavdotnet
gavdotnet

Reputation: 2224

You don't need to use NSNotificationCenter, NSUserDefaults or global variables.

As long as the view controllers are related (and looking at the OP's question, they certainly seem to be) you can simply set the view controllers up to hold a reference to each another (with one of the references being weak of course in order to avoid a "retain", or "strong reference", cycle). Then each view controller can set the property on the other view controller as needed. Example follows...

NB: This concept is valid for any two related view controllers. However, the following code assumes that:

  • The view controllers in question are related via a navigation controller and the second view controller is attached to the first via a push segue.
  • iOS 5.0 or above is in use (as it makes use of storyboards).

FirstViewController.h

@interface FirstViewController : UIViewController

/* Hold the boolean value (or whatever value should be
   set by the second view controller) in a publicly
   visible property */
@property (nonatomic, assign) BOOL someBooleanValue;

/* Provide a method for the second view controller to 
   request the first view controller to dismiss it */
- (void)dismissSecondViewController;

@end

FirstViewController.m

#import "FirstViewController.h"
#import "SecondViewController.h"

@implementation FirstViewController

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    /* Get the reference to the second view controller and set
       the appropriate property so that the secondViewController
       now has a way of talking to the firstViewController */
    SecondViewController *vc = [segue destinationViewController];
    vc.firstViewController = self;
}

- (void)dismissSecondViewController
{
    // Hide the secondViewController and print out the boolean value
    [self.navigationController popViewControllerAnimated:YES];    
    NSLog(@"The value of self.someBooleanValue is %s", self.someBooleanValue ? "YES" : "NO");
}

@end

SecondViewController.h

#import "FirstViewController.h"

@interface SecondViewController : UIViewController

// Create a 'weak' property to hold a reference to the firstViewController 
@property (nonatomic, weak) FirstViewController *firstViewController;

@end

SecondViewController.m

@implementation SecondViewController

/* When required (in this case, when a button is pressed),
   set the property in the first view controller and ask the
   firstViewController to dismiss the secondViewController */
- (IBAction)buttonPressed:(id)sender {
    self.firstViewController.someBooleanValue = YES;
    [self.firstViewController dismissSecondViewController];
}

@end

Of course, the most correct way to handle this sort of inter-viewController communication is to use protocols/delegates/data sources so that the SecondViewController doesn't need to know the specifics of its parent/owner object. However, sometimes it is quicker/simpler to build a solution like this just to prove the concept. Then if all is well and the code is worth keeping, refactor to use protocol(s).

In the case where view controllers don't - and shouldn't - know about each other, it may be necessary to use NSNotificationCenter. Don't use global variables or NSUserDefaults for communication between view controllers.

Upvotes: 1

Feel Physics
Feel Physics

Reputation: 2783

There is View-independent value keeping tool. You can use:

[[NSUserDefaults standardUserDefaults]setObject:<#(id)#> forKey:<#(NSString *)#>]

For example, you inputs strings or datas in A view, you can store them in above variables. And then, in B view, you can use them by below code:

 [[NSUserDefaults standardUserDefaults]objectOrKey:<#(NSString *)#>]

These are a example of NSUserDefaults data using:

ss

View A:

- (void)textFieldDidEndEditing:(UITextField *)sender
    {
        if (sender == homepage) {
            [[NSUserDefaults standardUserDefaults]
             setURL:[NSURL URLWithString:homepage.text] forKey:Ever5secHomepagePrefKey];
            if( [homepage canResignFirstResponder] ) {
                [homepage resignFirstResponder];   
            }
        } else if (sender == userId) {
            [[NSUserDefaults standardUserDefaults]
             setObject:userId.text forKey:Ever5secUserIdPrefKey];
objectForKey:Ever5secUserIdPrefKey]);
            if( [userId canResignFirstResponder] ) {
                [userId resignFirstResponder];   
            }
        } else if (sender == password) {
            [[NSUserDefaults standardUserDefaults]
             setObject:password.text forKey:Ever5secPasswordPrefKey];
            if( [password canResignFirstResponder] ) {
                [password resignFirstResponder];   
            }
        }
    }

View B:

userId.text = [[NSUserDefaults standardUserDefaults]
               objectForKey:Ever5secUserIdPrefKey];
password.text = [[NSUserDefaults standardUserDefaults]
                 objectForKey:Ever5secPasswordPrefKey];
homepage.text = [[[NSUserDefaults standardUserDefaults]
                  URLForKey:Ever5secHomepagePrefKey]
                 description];

Upvotes: 2

Popeye
Popeye

Reputation: 12093

This is not a good encapsulation answer but without being able to use protocols or delegates I don't believe it will have good encapsulation.

You can also create a global variable that you can set in one view controller and access in another.

ViewControllerOne.h

  extern NSString *globalVariable;

  @interface ViewControllerOne

  @end

ViewControllerOne.m

 #import "ViewControllerOne.h"

 @implementation ViewControllerOne

 NSString *globalVariables = @"Some String in the variable to access in second controller";

 @end

ViewControllerTwo.m

 #import "ViewControllerTwo.h"
 #import "ViewControllerOne.h"

 @implemetation ViewControllerTwo

 - (void)viewDidLoad
 {
     NSLog("%@", globalVariables);
 }

 @end

This will print out into the console

 ****CONSOLE****
 Some String in the variable to access in second controller

Upvotes: 3

Mazyod
Mazyod

Reputation: 22559

A question on this topic should really be focused more on NSNotificationCenter rather than NSUserDefaults, taking note that both are singletons.

NSUserDefaults:

The purpose of this class is NOT to pass variables between classes. It's purpose is, well, to store user's defaults. (ie preferences, settings, ... etc).

NSNotificationCenter:

This class is very handy, and has many different uses, one of which is to broadcast a variable for any class to receive. The receiving class is called the observer. This pattern is known as the Observer Pattern.

NOTE: The NSUserDefaults approach has the advantage of allowing you to set the variable before the other class is initialized, and can be retrieved at anytime. However, that's really sloppy (IMHO) and considered bad practice.


Quick and Dirty code sample on NSNotificationCenter:

// upon initializing the class that wants to observe the changes, we add it as an observer.
// So, somewhere in the A.m, upon being initialized (init, maybe?).

- (id)init {
    if (self = [super init]) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(calledUponNotif:)
                                                     name:@"MyObserveKey"
                                                   object:nil];
    }
    return self;
}

// the selector should look something like this:
- (void)calledUponNotif:(NSNotification *)notif {
    id sentVar = [notif object];
}

// Somewhere in the B.m
    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyObserveKey"
                                                        object:varToSend];

Another note: After calling the postNotification method, the registered selector in the other class will be called synchronously, so you don't have to worry about that.

Upvotes: 7

Related Questions