views:

1733

answers:

1

Hi All,

My problem is in getting the information from a controller(which happens to be my rootViewController) to another view. In trying to get access to it through the app delegate, I was not able to get it working. I have found how to do this, but this inturn created another problem of getting the view inside a modal view controller to actually display the data. Below I have posted both the appDelegate information, and an NSMutable Dictionary solution code for those that may need help too.

I had tried for more than a week to solve ths problem on my own. My issue ended up being how to access the appDelegate, which was why I was having a problem with the NSDictionary. So in the end the issue wasn't the NSDictionary, although had I gone further it would have been a problem I am sure.

Firstly I would like to thank TechZen for helping me to see I was over programming, and point me in the right direction.

Here is what I learned.

Assign your variable in the appDelegate.

AppDelegate.h

 @interface AppDelegate : NSObject  < UIApplicationDelegate, UINavigationControllerDelegate >
{   
    UIWindow *window;
    UINavigationController *navController;

    // Array to store the Makers Objects
    NSMutableArray *makers;

}

@property (nonatomic, retain) IBOutlet UIWindow *window; 
@property (nonatomic, retain) IBOutlet UINavigationController *navController;

@property (retain, nonatomic) NSMutableArray *makers;
@end

AppDelegate.m

    - (void)applicationDidFinishLaunching:(UIApplication *)application 
{

    makers = [[NSMutableArray alloc] init] ;
}

In the ViewController.m assign the variable to the appDelegate. I did this inside a tableView function didSelectRowAtIndexPath.

        AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

// The line below loads it into the variable, the one above takes it and makes it available for other view Controllers.

       Maker *maker = (Maker *)[appDelegate.makers objectAtIndex:indexPath.row];

// the combination of the above and below also loads the information into another array in this view controller, defined in an NSObject Controller called Maker (.h and .m files)

      maker = [self.Makers objectAtIndex:indexPath.row];

Now in your view Controller you want to load the variable from the appDelegate, set it up like this.

#import <UIKit/UIKit.h>

#import "AppDelegate.h"
#import "Maker.h"

@class AppDelegate;

@interface DetailsViewController : UIViewController
{

        AppDelegate *dappDelegate;
    DetailsViewController *detailsView;

    IBOutlet UITextView *makerDescription;

}

@property (retain, nonatomic) AppDelegate *dappDelegate;

@property (nonatomic, retain) DetailsViewController *detailsView; 

@property (nonatomic, retain) IBOutlet UITextView *makerDescription;

@end

and in the viewController.m file;

#import "DetailsViewController.h"
#import "AppDelegate.h"

@synthesize dappDelegate;

- (void)viewWillAppear:(BOOL)animated // or ViewDidLoad not sure which is better.

    dappDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
    NSString *newLocalVariable = [dappDelegate.makers description];

NSLog(@"newLocalVariable: %@", [dappDelegate.makers description]);
// This is for verifying you have it loaded. 'description' is defined in the Maker NSObject, see below for those files, and above for where it was assigned originally

..... and assign it to what ever you want now!

I hope this helps everyone. You could at this point drop the NSArray from there into a NSDictionary, but the access is now with keys and values, so a bit more complicated at this point to access, but of course advantages. I just cannot get that totally down yet, and have backed away from that method to just use a NSArray for now.

Below is a sample of a Makers h and m file for you to see as well.

Maker.h

@interface Maker : NSObject 
{
    NSString *name;
    NSString *address;
    NSString *city;
    NSString *postalcode;
    NSString *country;
    NSString *phonenumber;
    NSString *email;
    NSString *description;
    NSString *services;
    NSString *website;
}

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *address;
@property (nonatomic, copy) NSString *city;
@property (nonatomic, copy) NSString *postalcode;
@property (nonatomic, copy) NSString *country;
@property (nonatomic, copy) NSString *phonenumber;
@property (nonatomic, copy) NSString *email;
@property (nonatomic, copy) NSString *description;
@property (nonatomic, copy) NSString *services;
@property (nonatomic, copy) NSString *website;

- (id)initWithName:(NSString *)n address:(NSString *)a city:(NSString *)c postalcode:(NSString *)z country:(NSString *)o phonenumber:(NSString *)p email:(NSString *)e description:(NSString *)d services:(NSString *)s website:(NSString *)w;

@end

and its Maker.m file;

#import "ViolinMaker.h"

@implementation Maker
@synthesize name, address, city, postalcode, country, phonenumber, email, description, services, website;

- (id)initWithName:(NSString *)n address:(NSString *)a city:(NSString *)c postalcode:(NSString *)z country:(NSString *)o phonenumber:(NSString *)p email:(NSString *)e description:(NSString *)d services:(NSString *)s website:(NSString *)w; 
{
    self.name = n;
    self.address = a;
    self.city = c;
    self.postalcode = z;
    self.country = o;
    self.phonenumber = p;
    self.email = e;
    self.description = d;
    self.services = s;
    self.website = w;
    return self;
}
@end

I hope this helps others get this straight, as it really cost me a lot of time and I hope you can get a bit from what I learned.

Sincerely, Kirk

+2  A: 

I don't see any point where you populate the selectedMaker ivar in the DetailsViewController with data from the app delegate's `selectedMaker'. Just because they have the same name doesn't mean they share the same data.

You need to assign or copy the values from the app delegate to the view controller. The quick and dirty way is to do something like:

@implementation DetailsViewController

...

-(void) viewDidLoad{
//selectedMaker=[[UIApplication sharedApplication] selectedMaker]; <-- this is wrong
//Edit, this is the correct call to app delegate
selectedMaker=[[[UIApplication sharedApplication] delegate] selectedMaker]; 
}

Edit01:

So... at the suggestion of TechZen i have tried to move my NSDictionary out of my RootViewController.

(1) I'm not sure why you have the SelectedMaker class of what it is supposed to accomplish. You seem to have confused the class with a NSMutableDictionary. The class doesn't have any apparent reason to return a dictionary containing the values of it's own iVars. This is completely redundant. You already appear to have a class called ViolinMaker that encapsulates all the data for each violin maker record.

(2) The initializations methods for SelectedMaker are not implemented correctly. It looks like you have to call -[SelectedMaker initWithsName:..] before you call -[SelectedMaker init] otherwise init has no clue what the keys are.

(3) In any case, in the didSelectRow method, you don't actually initialize an instance of SelectedMaker. This line:

SelectedMaker *selectedMaker = [[[NSMutableDictionary alloc]retain] initWithObjects: objects forKeys: keys];

Does not create an instance of SelectedMaker but rather a NSMutableDictionary and more or less cast it to the SelectedMaker class. This is why you get the compiler warning from the for-loop.

(4) I don't see any need for the SelectedMaker class at all. These lines:

ViolinMakerAppDelegate *appDelegate = (ViolinMakerAppDelegate *)[[UIApplication sharedApplication] delegate];
ViolinMaker *violinMaker = (ViolinMaker *)[appDelegate.violinMakers objectAtIndex:indexPath.row];
violinMaker = [self.filteredViolinMakers objectAtIndex:indexPath.row];

Appear to provide you all the information you need to populate any particular row in your table or your detail view. You can use these three line in any view in which you need to access the data in appDelegate.violinMakers'. The violinMaker`object contains all the data you need.

I think you are making this more complicated than it has to be. All you need is (A) a class that encapsulates the data for each record fetched from your SQL. In this case it looks like ViolinMaker does this. (B) You need an array (or other collection) in the app delegate to store the multiple ViolinMaker instances. (C) You need code in each viewcontroller that will access the array/collection in the app delegate so that the viewcontroller can select the ViolinMaker instances it needs.

Edit02:

no I cannot use those 3 lines, as the 'objectAtIndex:indexPath.row' is only available inside the didSelectRow function. Which means I would have to reconstruct the table and all of its data.

You just define the dictionary once as an instance variable in your app delegate. So, you would have a mutable dictionary where each value was ViolinMaker and each key was some attribute of a violin maker such as the name. Let's call that violinMakersDict.Then, anywhere in your app, you would access the dictionary by first calling the app delegate and the accessing the violinMakersDict.

To populate a table, you would need to extract the some values as an array. Most likely, you would takes the keys which are names of violin makers. Then you would sort the array alphabetically, then you would populate each row with the value in the array at the index.row value.

Likewise, if you need to add data in one view, you can write to the violinMakersDict and then access that data from another view by again calling the app delegate.

TechZen
Very cool, and that is exactly what i am asking for, as I did feel I was instantiating a new variable, and it just had the same name... But how do I load the values into the appDelegate?? sorry, I am just not getting how to load those values into the Appdelegate? Thanks very much!Kirk
Digiguy
You need to check the the checkmark next to this answer to flag it as the answer you accepted. This will help your acceptance rate and make people more willing to spend time answering your questions in the future.
TechZen
Sorry, not used to this site, I really appreciate your help TechZen.My issue is that I load the NSMuteableDictionary from the RootViewController(a UITableViewController, inside tableView didSelectRowAtIndexPath), not inside the appDelegate. I can load with info there, I just don't get how to get it into another view from there. I see what you are saying there, but it simply says yellow warning 'UIApplication may not respond to -selected Maker'
Digiguy
If the data is in the rootViewController you need to assign the array to the subcontroller before you push it onto the navigation stack. (I can provide an example later if you request it.) As a rule you don't keep data in a view controller. There should be separate object that holds the data. This is especially true if more than one controller has to access the data. The app delegate is usually the best place to park it. Read up on the Model-View-Controller design pattern. It's very important to the understanding how all objective-c based APIs work.
TechZen
I know what you mean. I did that 4 my custom cells for my UITable. But if I was to create the dictonary as here, http://www.roseindia.net/tutorial/iphone/examples/nslog/nslogndictionary.html I cannot access a didSelectRow function called inside my RootViewController.m table access function for the objects. I have to do that where the NSDictionary code lays. IE [violinMaker name] I just need access that NSMutableDictionary made in the RootViewcontroller.h/m file. Also this means I cannot get the value created in the appdelegate either.Do you see my problem or do I just not see the answer?
Digiguy
You need to keep the data in your application delegate instead of any controller. You can call the delegate from anywhere in the app. Ideally, you would have a custom object to handle your data and just have a reference to it in the app delegate. You usually don't bother if your data is just a simple array but as your model becomes more complex you need a custom class to manage it..
TechZen
no I cannot use those 3 lines, as the 'objectAtIndex:indexPath.row' is only available inside the didSelectRow function. Which means I would have to reconstruct the table and all of its data. If the first two lines are actually loading the value into the *appDelete variable, what code do I use to access this value inside another viewController? Thanks again for your detailed help TechZen.
Digiguy
Hey TechZen. I seem to have made progress, and have ditched the NSMutableDictionary, as what you pointed out makes sense, and it isn't necessary for many reasons to approach it that way. Anyhoo... if you can suggest what I am missing now to access the variable in another controller, that would be great. I have no errors on my code, but not sure why the variable is not being passed, see above for what I have now for accessing it in the controller. I am really wondering if there is something I should have in the h file I am missing. The color coding in the m file seems good...
Digiguy