ctatti
ctatti

Reputation: 170

objectForKey returns nil on a dictionary

I am creating a recipe manager app for iPad. I am using the split view template which loads data for the recipes from a plist which is structured in this way. The table view on the left works perfectly. It loads the data from the plist. The problem is the detail view. As you can see I have inserted a NSLog lines in the DetailViewController.m file since every string I passed to the text field resulted as nil. Strangely, when I tried to NSLog from the RootViewController.m with: NSLog(@"%@", [detailViewController.ricetta allKeys]) it worked.

Any suggestions?

I tried to post as much lines as possible to explain the problem as well as possible.

<plist version="1.0">
    <array>
        <dict>
            <key>name</key>
            <string>Risotto alla zucca</string>
            <key>ingredients</key>
            <string></string>
            <key>description</key>
            <string></string>
            <key>photo</key>
            <string></string>
        </dict>
        <dict>
            <key>name</key>
            <string>Semi di zucca al forno</string>
            <key>ingredients</key>
            <string></string>
            <key>description</key>
            <string></string>
            <key>photo</key>
            <string></string>
        </dict>
        <dict>
            <key>name</key>
            <string>Risotto ai funghi</string>
            <key>ingredients</key>
            <string></string>
            <key>description</key>
            <string></string>
            <key>photo</key>
            <string></string>
        </dict>
    </array>
</plist>

RootViewController.h

#import <UIKit/UIKit.h>

#import "RecipeConstants.h"

@class DetailViewController;

@interface RootViewController : UITableViewController {
    NSMutableArray *ricette;
}

@property (nonatomic, retain) NSMutableArray *ricette;

@property (nonatomic, retain) IBOutlet DetailViewController *detailViewController;

@end

RootViewController.m

...
@synthesize ricette;

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.clearsSelectionOnViewWillAppear = NO;
    self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);

    NSString *path = [[NSBundle mainBundle] pathForResource:@"RicetteArray" ofType:@"plist"];
    NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
    self.ricette = tmpArray;
    [tmpArray release];
}
...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.ricette count];

}
    // Configure the cell.

    cell.textLabel.text = [[self.ricette objectAtIndex:indexPath.row] objectForKey:NAME_KEY];
    return cell;
}
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    detailViewController.ricetta = [self.ricette objectAtIndex:indexPath.row];    
}
...
- (void)dealloc
{
    [detailViewController release];

    [ricette release];

    [super dealloc];
}
@end

DetailViewController.h

#import <UIKit/UIKit.h>

#import "RecipeConstants.h"

@interface DetailViewController : UIViewController <UIPopoverControllerDelegate, UISplitViewControllerDelegate> {

    IBOutlet UILabel *lblTitolo;
    IBOutlet UITextView *ingredientiTxtView;
    IBOutlet UITextView *descrizioneTxtView;
    IBOutlet UIImageView *fotoImgView;

    NSDictionary *ricetta;
}

@property (nonatomic, retain) IBOutlet UILabel *lblTitolo;
@property (nonatomic, retain) IBOutlet UITextView *ingredientiTxtView;
@property (nonatomic, retain) IBOutlet UITextView *descrizioneTxtView;
@property (nonatomic, retain) IBOutlet UIImageView *fotoImgView;
@property (nonatomic, retain) NSDictionary *ricetta;

@property (nonatomic, retain) IBOutlet UIToolbar *toolbar;
@property (nonatomic, retain) id detailItem;

@end

DetailViewController.m

@synthesize lblTitolo, ingredientiTxtView, descrizioneTxtView, fotoImgView, ricetta;
...
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    lblTitolo.text = [ricetta objectForKey:NAME_KEY];
    ingredientiTxtView.text = [ricetta objectForKey:INGREDIENTS_KEY];
    descrizioneTxtView.text = [ricetta objectForKey:DESCRIPTION_KEY];
    UIImage *img = [UIImage imageNamed:PHOTO_KEY];
    fotoImgView.image = img;

    NSLog(@"%@", [ricetta allKeys]);
}
...
- (void)dealloc
{
    [_myPopoverController release];
    [_toolbar release];
    [_detailItem release];

    [lblTitolo release];
    [ingredientiTxtView release];
    [descrizioneTxtView release];
    [fotoImgView release];
    [ricetta release];

    [super dealloc];
}

@end

UPDATE: Actually in the DetailViewController.m I have this method:

- (void)updateRicetta {
    lblTitolo.text = [ricetta objectForKey:NAME_KEY];

    NSString *myIngredients = [ricetta objectForKey:INGREDIENTS_KEY];
    myIngredients = [myIngredients stringByReplacingOccurrencesOfString:@", " withString:@"\n"];
    ingredientiTxtView.text = myIngredients;

    NSString *myDescription = [ricetta objectForKey:DESCRIPTION_KEY];
    myDescription = [myDescription stringByReplacingOccurrencesOfString:@". " withString:@"\n"];
    descrizioneTxtView.text = myDescription;

    NSString *nomeImmagine = [NSString stringWithFormat:@"%@.jpg", [ricetta objectForKey:PHOTO_KEY]];
    [self.fotoImgView setImage:[UIImage imageNamed:nomeImmagine]];
}

which is called from RootViewController.m in

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

using

[detailViewController updateRicetta];

I have the following two warnings:

'DetailViewController' may not respond to '-updateRicetta'
Instance method '-updateRicetta not found (return type defaults to 'id')

Another problem is that the detail view updates only when I click on a cell not at app loading.

Upvotes: 1

Views: 652

Answers (1)

Maurice Kelly
Maurice Kelly

Reputation: 1472

I'm happy to be wrong about this, but it looks to me like when you call tableView:didSelectRowAtIndexPath: you set the recipe (ricetta?) in the DetailViewController but I don't see any instruction to tell the DetailViewController to display the new recipe.

You have code to update your DetailViewController in viewWillAppear:animated: but this will only happen when the view is going to appear - if your view has already appeared this method won't be executed.

I suggest you create a method in DetailViewController called updateRicetta which will look something like:

- (void)updateRicetta {
    lblTitolo.text = [ricetta objectForKey:NAME_KEY];
    ingredientiTxtView.text = [ricetta objectForKey:INGREDIENTS_KEY];
    descrizioneTxtView.text = [ricetta objectForKey:DESCRIPTION_KEY];
    UIImage *img = [UIImage imageNamed:PHOTO_KEY];
    fotoImgView.image = img;
}

You could then call this from tableView:didSelectRowAtIndexPath: -

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    detailViewController.ricetta = [self.ricette objectAtIndex:indexPath.row];
    [detailViewController updateRicetta];
}

As to the returning nil: my guess is that when viewWillAppear:animated: is called you have not yet selected an entry and therefore ricetta has not yet been set in the DetailViewController.

Upvotes: 1

Related Questions