0

I try adding objects to NSMutableArray from another class (secondViewController) and then add it to my UITableView in my FirstViewController, but it returns null when I print it using NSLog. Here is my set up.

FirstViewController.h:

@interface FirstViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>{
    IBOutlet UITableView *mytableview;
    NSMutableArray *mytableinfo;
}
@property (nonatomic,retain) IBOutlet UITableView *mytableview;
@property (retain) IBOutlet NSMutableArray *mytableinfo;

FirstViewController.m

#import "FirstViewController.h"
#import "SecondViewController.h"
@synthesize mytableinfo,mytableview;
-(IBAction)addShift:(id)sender{
SecondViewController *secondViewController = [[SecondViewController alloc]init];
[self presentModalViewController:secondViewController animated:YES];
}
- (void)viewDidLoad
{
    mytableinfo = [[NSMutableArray alloc]init];
    [super viewDidLoad];
}

SecondViewController.m

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

@implementation SecondViewController
@synthesize dateformatter,mydatepicker,startingTime;

-(IBAction)saveShift:(id)sender{
    FirstViewController *firstViewController = [[FirstViewController alloc]init];
    [firstViewController.mytableinfo addObject:@"Hello world"];
    NSLog(@"%@",firstViewController.mytableinfo);
    [self dismissModalViewControllerAnimated:YES];
}

My goal is to ultimately feed a mytableviewfrom mytableinfo. I'm not even sure if this is the best way to go about it. Any advice would be appreciated.

bryanmac
  • 38,941
  • 11
  • 91
  • 99
sam
  • 117
  • 1
  • 13
  • 1
    If you are expecting to see the string @"Hello world" that you just added to firstViewController.mytableinfo, then your code should read NSLog(@"%@", [firstViewController.mytableinfo objectAtIndex:0]); or NSLog(@"%@", [firstViewController.mytableinfo lastObject]); – eric.mitchell Nov 04 '11 at 02:34
  • @Rickay Thanks for your comment mate. I learned something new from you today. However it's still returning null. – sam Nov 04 '11 at 06:21

5 Answers5

4

In SecondViewController, you are creating a FirstViewController with alloc init. At that point, mytableinfo on FirstViewController is nil because you don't allocate until viewDidLoad.

What loads SecondViewController? Because you're dismissing it modally. If it's FirstViewController, then when you alloc init first view controller, you're not calling the instance that presented it modally.

It's also not very MVC to have one view poke at another like that. It creates code that's couple at the view layer and modifying data at the view layer. It is better to create a model and have both views modifying that model.

How to create a NSMutable Array which can access from different view controllers

Another way to communicate between views is for one view to pass a delegate (a callback) to the other view. That allows the other view to not be coupled to the other view - it only knows about the protocol for the delegate.

What exactly does delegate do in xcode ios project?

Community
  • 1
  • 1
bryanmac
  • 38,941
  • 11
  • 91
  • 99
  • in my `FirstViewController` I have a system add button on the top right hand corner when tapped it calls `addShift` Which looks like this. here `-(IBAction)addShift:(id)sender{ SecondViewController *secondViewController = [[SecondViewController alloc]init]; [self presentModalViewController:secondViewController animated:YES]; }` – sam Nov 04 '11 at 06:24
  • Here is my [project file](http://burnedcodes.com/wp-content/uploads/2011/11/AnotherTimeSheet.zip). If you'd like to have a look at my code. – sam Nov 04 '11 at 06:50
2

There is a point that look strange to me in your "SecondViewController" you dissmiss it like it's a modal. My Question is then... who started the modal presentation? A "FirstViewController"? If it's the case, why are you creating a new one, on dismissing the second, the First that launched it will resume it's activity.
An other thing that I don't understand is that the designated initializer for a UIViewController is

- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle

You can pass nil to both argument, if no nib need to be associated with.

And finaly, if you need to get back a NSMutableArray to a 1st ViewController (VC) from a 2nd VC that was modally presented by the 1st you can do this in the 2nd VC:

- (id)initWithMutableArray:(NSMutableArray *)theArray    {
//... put standard init code here   }

And make that the default initializer of your second VC. But this make sense only if 2nd VC absolutely need a mutable array.


And now for my curiosity because I don't understand this line

@property (retain) IBOutlet NSMutableArray *mytableinfo;

Why is this an IBOutlet? That look like a potential source of problem. IBOutlet are usually pointers to UI elements in a xib file.

Vincent Bernier
  • 8,674
  • 4
  • 35
  • 40
  • OK. I have edited my question with further details on how the secondveiwcontroller comes in. Thanks for the heads up on the @property line. I'm just following things that I see and read on websites. As you can see some aren't all that reliable. Here is al ink to my project if you'd like to have a look at the whole thing. [Download xCode Project](http://burnedcodes.com/wp-content/uploads/2011/11/AnotherTimeSheet.zip) – sam Nov 04 '11 at 06:48
  • Ok, but you still have the problem that in the SecondView your creating a new firstview, when you dismiss the SecondView that view you've created will be gone to go back to the first firstView (the one that launched the modalPresentation). So what append in that firstView is irrelevant, but for the sake of argument, like an other one said, you are sending your addObject method to a "nil" pointer. In objective-C that is not a reason to crash, in Java that call would make your app crash. – Vincent Bernier Nov 04 '11 at 14:25
  • I wish I could pick two answers as the solution. You helped me out a lot with mentioning the @property. it happened to be the source of the issue... well for the most part. thanks again. – sam Nov 05 '11 at 04:03
1

When populating a UITableView with an array that can be modified by multiple modal views during the course of your app, I find one of the best ways to do this is with NSUserDefaults. You can create an NSUserDefaults object for reference like this:

NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];

Then you can assign objects to each key in defaults, which is really just a plist (which is just a list of keys with objects associated with them.

So then, when you want to store the array in defaults, you can say:

[defaults setObject:mytableinfo forKey:@"tableInformationKey"];

Then, whenever you want to access that data, you can say:

NSMutableArray* tableInfoCopy = [defaults mutableArrayValueForKey:@"tableInformationKey"];

That will make you a copy of the array you have stored in NSUserDefaults (NSUserDefaults can be accessed from anywhere in your app), so then you can make changes to that mutable array you just made. Once you are done making changes, you can reassign it to NSUserDefaults like this:

[defaults setObject:tableInfoCopy forKey@"tableInformationKey"];

So when you populate your UITableView, in

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

put something like:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Foobar"];
    if (cell == nil) {
        // No cell to reuse => create a new one
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Foobar"] autorelease];

        // Initialize cell with some customization
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
        cell.textLabel.textColor = [UIColor blackColor];
    }

    NSArray* arrayOne = [defaults objectForKey:@"tableInformationKey"];

    NSString* title = [arrayTwo objectAtIndex:indexPath.row]; 
    //this goes to the index in the array of whatever cell you are
    // at, which will populate your table view with the contents of this array (assuming the array contains strings)

    // Customize cell
    cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:25];

    return cell;
}
eric.mitchell
  • 8,817
  • 12
  • 54
  • 92
  • 1
    Apple recommend that you don't use NSUserDefault in that fashion, but that you mimic it's way by implementing a "singleton instance" instead. That should be more efficient and will give you some lazy initialization. – Vincent Bernier Nov 04 '11 at 15:39
  • The code looks very promising. I would try it tonight and see how it goes. @VinceBurn Right now I'm at a stage so desperate to have a working app that apple's recommendations are the last of my concern. Of course the details like this can be edited at a later stage. Thanks for the heads up. – sam Nov 04 '11 at 20:24
  • @VinceBurn do you have a link to that documentation? I'd like to take a look at it – eric.mitchell Nov 04 '11 at 20:35
  • @Rickay I don't recall the document for the singleton, make a search and you should find it. As for not using NSUserDefault, it was in a video from a class given by Apple's employ at the Standford University. It's on iTunesU. The problem with NSUserDefault is that if you got a lot of heavy object, they will all be bring into memory even if you just check a flag on launchUp. That can lead to a huge performance hit. – Vincent Bernier Nov 05 '11 at 04:40
  • Interesting. The only reason I gravitate towards NSUserDefaults because it's easy persistent data, which is exactly what I want. I guess I could store it in a text file, but would that not bring up the same problems as those you stated above? – eric.mitchell Nov 05 '11 at 14:52
0

Use them the easiest way is to put the array in your AppDelegate

// populate appDelegate's array
AppDelegate *appDelegate = (myAppDelegate *) [[UIApplication sharedApplication] delegate];  
[appDelegate.arrayMyTableInfo addObject:@"HelloWorld"];
Alex Moskalev
  • 607
  • 7
  • 15
0

Here is how you can do what I've suggested : (This is part of your code updated)
This is in your secondView, this is one of many way to pass the array to your second view.

@synthesize array4Test;
- (id)initWithMutableArray:aMutableArray
{
self = [self initWithNibName:nil bundle:nil];
if (self)
{
    self.array4Test = aMutableArray;
}
return  self;
}
- (void)dealloc 
{    
//  HERE clean up the property is set to retain.
self.array4Test = nil;
[super dealloc];
}

Here is the code for the firstView

-(IBAction)addShift:(id)sender{
SecondViewController *secondViewController = [[SecondViewController alloc] initWithMutableArray:self.mytableinfo];
[self presentModalViewController:secondViewController animated:YES];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.mytableview reloadData];
NSString *aString = [mytableinfo lastObject];
if (aString)
{
    NSLog(@"This just came back from the second View\n%@", aString);
}
}
Vincent Bernier
  • 8,674
  • 4
  • 35
  • 40