2

I made a very simple storyboard based project with two View Controllers.

I want to simply access a string declared in VC1 from VC2. The second VC should then display the text in a textfield upon the press of a button.

I do not want to use delegation, a separate class for global data or global variables and Extern. Instead, I read that it was easy to achieve variable sharing using a reference to one VC in the other.

For my code shown below, XCode didn't complain, however my problem is this: The NSLog in the second VC returns null.

If anybody can tell me how to amend the code to pass the string to the second VC/ tell me where I'm going wrong I would appreciate it.

VC1 Header:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property NSString* textToPassToOtherVC;

VC1 Implementation:

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
@synthesize textToPassToOtherVC = _textToPassToOtherVC;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    _textToPassToOtherVC = @"Here is some text";
    NSLog (@"Text in VC1 is: %@", _textToPassToOtherVC);
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return YES;
}

@end

VC2 Header:

#import <UIKit/UIKit.h>

@class ViewController;

@interface ViewController2 : UIViewController

@property (nonatomic, strong) ViewController *received;

@property (strong, nonatomic) IBOutlet UITextField *textDisplay;

- (IBAction)textButton:(id)sender;

@end

VC2 Implementation:

#import "ViewController2.h"
#import "ViewController.h"

@interface ViewController2 ()

@end

@implementation ViewController2
@synthesize textDisplay;
@synthesize received;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

}

- (void)viewDidUnload
{
    [self setTextDisplay:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return YES;
}

- (IBAction)textButton:(id)sender {

    NSLog (@"Text in VC1 from VC2 is: %@", self.received.textToPassToOtherVC);

    textDisplay.text = self.received.textToPassToOtherVC;
}
@end
Pshemo
  • 122,468
  • 25
  • 185
  • 269
Dave Chambers
  • 2,483
  • 2
  • 32
  • 55
  • 2
    Where are you setting "received" in ViewController2? – mamackenzie Aug 04 '12 at 00:22
  • Hi Michael,I am not setting "received" but that is supposed to be the reference to VC1, therefore in this case the code self.received.textToPassToOtherVC; should give me the string 'Here is some text' – Dave Chambers Aug 04 '12 at 00:26
  • Where is "`_textToPassToOtherVC`" declared? Aside from it being referenced in your "`@synthesize`" line, I don't see it mentioned anywhere else. – Michael Dautermann Aug 04 '12 at 00:26
  • Hi Michael, maybe I'm way off here but the "_textToPassToOtherVC" is declared in VC1 and should be passed to VC2, or so I hoped. – Dave Chambers Aug 04 '12 at 00:28
  • I don't see "`_textToPassToOtherVC`" declared in VC1 though. [Either it should be a static in your "`.m`" file or an ivar in the "`.h`" file.](http://stackoverflow.com/questions/822487/how-does-an-underscore-in-front-of-a-variable-in-a-cocoa-objective-c-class-work) – Michael Dautermann Aug 04 '12 at 00:33
  • Michael is sort of right. What's weird is that this compiles (Obj-C does this for you), but you really don't even need the underscored instance variable if you aren't implementing some kind of customized logic under the hood. – mamackenzie Aug 04 '12 at 00:37
  • I had @property NSString* textToPassToOtherVC; in my VC1 h file. I just tried adding {NSString* textToPassToOtherVC;} in h but it didn't help. Pardon my ignorance but how do I make a static in the m file? – Dave Chambers Aug 04 '12 at 00:52
  • You havent answered Michael's original question. Nowhere in this code is `received` actually being set to anything. It's _declared_, but never given a value. How do you think it is being set? I think that's your central misunderstanding here, so if we can clear that up, you'll be good. – jrturton Aug 04 '12 at 07:30
  • Yes. I think you and Michael are correct. `received` is not set. OK, can anybody tell me **HOW** to set my label's text, via `textDisplay.text = ` to the first VC's `textToPassToOtherVC`. I think the question is quite simple. Thanks – Dave Chambers Aug 04 '12 at 09:05

4 Answers4

3

Your second view controller needs to have its received property set to a value that represents the first view controller.

With storyboard-based projects, this is typically done in prepareForSegue:. This method will be called in your first view controller before the segue to your second view controller is performed.

(In my opinion you would be better off passing just the string to your second view controller rather than a pointer to the whole view controller as this reduces dependency and is the only information your second view controller really needs, but let's not complicate things.)

Here are the steps I think you need to get this working:

  • Give the segue from your first view controller to your second a name in the storyboard. For this example, I'll call it mySegue.
  • Import "ViewController2.h" in "ViewController.m" - your first view controller will need to know that the second view controller has a received property.
  • Add a prepareForSegue: method like so in your first view controller:

    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
        if ([segue.identifier isEqualToString:@"mySegue"])
        {
            ViewController2 *targetVC = (ViewController2*)segue.destinationViewController;
            targetVC.received = self;
        }
    }
    

Hopefully it's pretty clear what this code is doing.

jrturton
  • 118,105
  • 32
  • 252
  • 268
  • Well, that answer is 100% crystal clear. Thank you so much. Now the code does exactly what I wanted. Answered. In addition, I agree that the string passing would be better. I added a property in the second VC `@property (nonatomic, strong) NSString* textToReceiveFromOtherVC;` I am unclear about how I would 'send' the string `textToPassToOtherVC` in VC1 to `textToReceiveFromOtherVC` in VC2. Could you tell me what code I'd need and where I'd need it please? Thanks – Dave Chambers Aug 04 '12 at 10:06
  • 2
    Instead of setting `targetVC.received = self`, you'd set `targetVC.textToReceive = self.stringToPass` or whatever the property was called. – jrturton Aug 04 '12 at 10:08
1

Your mistake is based on a fundamental misunderstanding of properties. Properties are merely syntactic sugar for dealing with the boilerplate and implementation details of getters and setters. Although setting _textToPassToOtherVC will indeed make the property return that value, it does not "inform" the other, because it's reference to "received" is set to nil by default, and never set by you.

If anywhere before you actually check the value of this shared text, you have the lines:

ViewController *myVC1; 
ViewController2 *myVC2;

// ...some initialization.

myVC2.received = myVC1;
// Now the IBAction will display the correct text.

Everything will work.

mamackenzie
  • 1,156
  • 7
  • 13
  • Hi Michael, thanks for the answer. I added ViewController *myVC1; ViewController2 *myVC2; to the m file, beneath the synthesize lines then myVC2.received = myVC1; to the textButton action and in the action swapped textDisplay.text = self.received.textToPassToOtherVC; for textDisplay.text = myVC2.received.textToPassToOtherVC; It still does not work. Any ideas? – Dave Chambers Aug 04 '12 at 01:08
  • No no, this was just the bare code to illustrate my principle. myVC1 and myVC2 need to be two actual view controllers, not random pointers. Where do you actually MAKE these objects? – mamackenzie Aug 04 '12 at 01:13
  • They are made in Interface Builder. Other than that, all the code for the project is included in what I posted. So, taking your example, I would write this: ViewController2.received = ViewController; Thing is, this doesn't compile due to error: property 'received' not found on ViewController2' – Dave Chambers Aug 04 '12 at 01:21
0

Have you checked that your self.received is not null/nil?

Set a breakpoint in the code to see if self.received is actually initialized (if it's not, then that's why you don't see anything):

- (IBAction)textButton:(id)sender {

    // Breakpoint here
    NSLog (@"Text in VC1 from VC2 is: %@", self.received.textToPassToOtherVC);

    textDisplay.text = self.received.textToPassToOtherVC;
}
Peter Warbo
  • 11,136
  • 14
  • 98
  • 193
  • bordering on a -1 here; this seems more of a question or comment and not an answer... – Michael Dautermann Aug 04 '12 at 00:27
  • Hi Peter,I'm very new to Objective C but I am moving between the VCs in the storyboards using segue's so I guess neither is nil – Dave Chambers Aug 04 '12 at 00:54
  • @DaveChambers Try setting a breakpoint in the code to debug and see what is the wrong. See my edit. – Peter Warbo Aug 04 '12 at 07:17
  • Yes Peter. You and the comments on my question have both hinted at the answer. `received` is not set. OK, can you tell me **HOW** to set my label's text, via `textDisplay.text = ` to the first VC's `textToPassToOtherVC`. Thanks – Dave Chambers Aug 04 '12 at 09:08
0

Try using an NSNotification. When you need to send the string, post a NSNotification with the object!

Put when you want to send the string (after you give the string a value of course):

    yourString = @"Hello";

    [[NSNotificationCenter defaultCenter] postNotificationName:@"sendString" object:yourString];

and in your second View Controller:

-(void)viewDidLoad:
{
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ThingYouWantToDoWithString:) name:@"sendString" object:nil];
}

- (void)ThingYouWantToDoWithString:(NSNotification *)notification{
        // Make sure you have an string set up in your header file or just do NSString *theString = [notification object] if your using ARC, if not allocate and initialize it and then do what I just said
        theString = [notification object];
        NSLog("%@", theString);
        [self performSelector:@selector(OtherThings:) object:theString];
}

Hope this helps!

sridvijay
  • 1,526
  • 18
  • 39
  • Hi, Thanks for the suggestion. I will try that tomorrow and let you know if it works. – Dave Chambers Aug 04 '12 at 01:12
  • @DaveChambers Sure thing, also just thought of this too, if that doesn't work try using the app delegate to share the string, I'll edit my answer with how to do that if the NSNotification doesn't work! – sridvijay Aug 04 '12 at 01:13
  • Why use a notification when one of the controllers has direct access to the other? The point of notifications is that the sending object doesn't know what others objects might be listening. – Caleb Aug 04 '12 at 01:19
  • @Caleb AFAIK, it's quick and easy on memory and they're no performance impacts and it gets the job done for short things like this where there isn't 20 notification to keep track of. Is there really any negative impacts by using this? – sridvijay Aug 04 '12 at 02:56
  • Just to inform, my question has now been answered but I'll try your approach if I wish to use NSNotification in future. – Dave Chambers Aug 04 '12 at 10:30
  • 1
    @Programmer20005 Imagine that you and I are sitting next to each other at a table, and I'd like some salt. I could ask you to pass the salt, or I could call our mutual friend and ask her to call you and ask you to move the salt to your right. Which seems simpler? The OP tells us that one controller will have direct access to the other, so ViewController2 can simply access `self.received.textToPassToOtherVC`. The OP needs only to ensure that ViewController2's `received` property is set up properly. – Caleb Aug 05 '12 at 04:05
  • @Caleb I see your point more clearly now, thanks for taking the time to explain! – sridvijay Aug 05 '12 at 04:23