ItemDetailViewController view disappears


#1

Everything was working peachy-keen at the end of chapter 12.

I implemented the picture taking code given in chapter 13 (have not done UIPopOver… yet), and suddenly, the text fields when I select the row no longer appear in the view. The background color is rendering properly, but the text fields, labels, and image view are all incorrect. Additionally, when I take a picture with the camera and select “use”, it loads what appears to be a blank XIB file. I am using XCode 4.0 (Build 4A304a) with the iOS 4.3 SDK. I’ve gone over my code four times to look for typos and issues that I might have had, but I can find none. I’m going to go ahead and post a number of my files here and see if anyone can help me determine what is going on. Maybe there is a typo I’m just not seeing, or I added or neglected to change something in the code that didn’t matter up until this point. I’m just stumped at this point.

ItemsViewController.h

//
//  ItemsViewController.h
//  Homepwner
//
//  Created by ME on 4/22/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <UIKit/UIKit.h>

@class ItemDetailViewController;

@interface ItemsViewController : UITableViewController {
    NSMutableArray *possessions;
    ItemDetailViewController *detailViewController;
}

@end

ItemsViewController.m

//
//  ItemsViewController.m
//  Homepwner
//
//  Created by ME on 4/22/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "ItemsViewController.h"
#import "Possession.h"
#import "ItemDetailViewController.h"


@implementation ItemsViewController


-(id) init
{   
    [super initWithStyle:UITableViewStyleGrouped];
    
    // Create an array of 10 random possesion objects
    
    possessions = [[NSMutableArray alloc] init];
    for (int i = 0; i < 10; i++) {
        [possessions addObject:[Possession randomPossession]];
   }
    
    [[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];
    
    [[self navigationItem] setTitle:@"Homepwner"];
    
    return self;
}

-(id)initWithStyle:(UITableViewStyle)style
{
    return [self init];
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    int numberOfRows = [possessions count];
    if ([self isEditing])
        numberOfRows++;
    
    return numberOfRows;
}


-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([self isEditing] && [indexPath row] == [possessions count]) {
        return UITableViewCellEditingStyleInsert;
    }
    
    return UITableViewCellEditingStyleDelete;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Check for a reusable cell of Identifier UITableViewCell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
    
    
    // If none exists, create a new one!
    if (!cell) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"] autorelease];
    }
    
    if ([indexPath row] < [possessions count]) {
        // Create a possession object from the array @ index supplied by method
        Possession *p = [possessions objectAtIndex:[indexPath row]];
        // Assign data to the current row
        [[cell textLabel] setText:[p description]];
    } else {
        [[cell textLabel] setText:@"Add New Item..."];
    }
    
    return cell;
}


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

- (void)dealloc
{
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
}
*/

/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
    [super viewDidLoad];
}
*/

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (!detailViewController) {
        detailViewController = [[ItemDetailViewController alloc] init];
    }
    
    [detailViewController setEditingPossession:[possessions objectAtIndex:[indexPath row]]];
    
    [[self navigationController] pushViewController:detailViewController animated:YES];
}

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear];
    [[self tableView] reloadData];
}

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    // If the table view is asking to commit a delete command
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        
        // remove the row in possessions
        [possessions removeObjectAtIndex:[indexPath row]];
        
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        [possessions addObject:[Possession randomPossession]];
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]withRowAnimation:UITableViewRowAnimationLeft];
    }
}

-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([indexPath row] < [possessions count])
        return YES;
    return NO;
}

-(NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
{
    if ([proposedDestinationIndexPath row] < [possessions count]) {
        return proposedDestinationIndexPath;
    }
    
    NSIndexPath *betterIndexPath = [NSIndexPath indexPathForRow:[possessions count]-1 inSection:0];
    return betterIndexPath;
    
}

-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
    // Get pointer to object being moved
    Possession *p = [possessions objectAtIndex:[sourceIndexPath row]];
    
    // retain p so that it is not deallocated when removed from the array
    [p retain];
    
    [possessions removeObjectAtIndex:[sourceIndexPath row]];
    
    [possessions insertObject:p atIndex:[destinationIndexPath row]];
    
    [p release];
}

-(void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    [super setEditing:editing animated];
    
    if (editing) {
        // If entering editing mode, add another row
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[possessions count] inSection:0];
        NSArray *paths = [NSArray arrayWithObject:indexPath];
        [[self tableView] insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationLeft];
    } else {
        // If leaving edit mode, we remove the last row
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[possessions count] inSection:0];
        NSArray *paths = [NSArray arrayWithObject:indexPath];
        [[self tableView] deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
    }
}

@end

ImageCache.h

//
//  ImageCache.h
//  Homepwner
//
//  Created by ME on 4/24/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>


@interface ImageCache : NSObject {
    NSMutableDictionary *dictionary;
}

+(ImageCache *)sharedImageCache;
-(void)setImage:(UIImage *)i forKey:(NSString *)s;
-(UIImage *)imageForKey: (NSString *)s;
-(void)deleteImageForKey: (NSString *)s;

@end

ImageCache.m

//
//  ImageCache.m
//  Homepwner
//
//  Created by ME on 4/24/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "ImageCache.h"

static ImageCache *sharedImageCache;

@implementation ImageCache

-(id) init
{
    [super init];
    dictionary = [[NSMutableDictionary alloc] init];
    return self;
}

#pragma mark Accessing the cache

- (void)setImage:(UIImage *)i forKey:(NSString *)s
{
    [dictionary setObject:i forKey:s];
}

- (UIImage *)imageForKey:(NSString *)s
{
    return [dictionary objectForKey:s];
}

-(void)deleteImageForKey:(NSString *)s
{
    [dictionary removeObjectForKey:s];
}

#pragma mark Singleton stuff

+(ImageCache *) sharedImageCache
{
    if (!sharedImageCache) {
        sharedImageCache = [[ImageCache alloc] init];
    }
    return sharedImageCache;
}

+(id)allocWithZone:(NSZone *)zone
{
    if(!sharedImageCache) {
        sharedImageCache = [super allocWithZone:zone];
        return sharedImageCache;
    } else {
        return nil;
    }
}

-(id)copyWithZone:(NSZone *)zone
{
    return self;
}

-(void) release
{
    // no op
}

@end

ItemDetailViewController.h

//
//  ItemDetailViewController.h
//  Homepwner
//
//  Created by Ritchie, Christian {PBSG} on 4/24/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <UIKit/UIKit.h>
@class Possession;


@interface ItemDetailViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate> {
    IBOutlet UITextField *nameField;
    IBOutlet UITextField *serialNumberField;
    IBOutlet UITextField *valueField;
    IBOutlet UILabel *dateLabel;
    
    Possession *editingPossession;
    IBOutlet UIImageView *imageView;
}

@property (nonatomic, assign) Possession *editingPossession;

@end

ItemDetailViewController.m

//
//  ItemDetailViewController.m
//  Homepwner
//
//  Created by ME on 4/24/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "ItemDetailViewController.h"
#import "Possession.h"
#import "ImageCache.h"

@implementation ItemDetailViewController

@synthesize editingPossession;


-(id) init{
    
    [super initWithNibName:@"ItemDetailViewController" bundle:nil];
    
    UIBarButtonItem *cameraBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:@selector(takePicture:)];
    
    [[self navigationItem] setRightBarButtonItem:cameraBarButtonItem];
    [cameraBarButtonItem release]; // It's retained by the navigation item! We don't need it.
    return self;
}
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
    return [self init];
}

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    [imageView setImage:image];
    [self dismissModalViewControllerAnimated:YES];
}

-(void)takePicture:(id)sender
{
    UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
    
    // check for a camera!
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        [imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
    } else {
        [imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
    }
    
    [imagePicker setDelegate:self];
    
    [self presentModalViewController:imagePicker animated:YES];
    
    [imagePicker release]; //image picker is retained by ItemDetailViewController. No longer needed.
}

- (void)dealloc
{
    [nameField release];
    [serialNumberField release];
    [valueField release];
    [dateLabel release];
    [imageView release];
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[self view] setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
    // Do any additional setup after loading the view from its nib.
}

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear];
    
    [nameField setText:[editingPossession possessionName]];
    [serialNumberField setText:[editingPossession serialNumber]];
    [valueField setText:[NSString stringWithFormat:@"%d", [editingPossession valueInDollars]]];
    
    // Create a date formatter (NSDateFormatter) to return a simple date string
    NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    [dateFormatter setTimeStyle:NSDateFormatterNoStyle];
    
    [dateLabel setText:[dateFormatter stringFromDate:[editingPossession dateCreated]]];
    
    [[self navigationItem] setTitle:[editingPossession possessionName]];
}

-(void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear];
    
    [nameField resignFirstResponder];
    [serialNumberField resignFirstResponder];
    [valueField resignFirstResponder];
    
    [editingPossession setPossessionName:[nameField text]];
    [editingPossession setSerialNumber:[serialNumberField text]];
    [editingPossession setValueInDollars:[[valueField text] intValue]];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    
    [nameField release];
    nameField = nil;

    [serialNumberField release];
    serialNumberField = nil;
    
    [valueField release];
    valueField = nil;
    
    [dateLabel release];
    dateLabel = nil;
    
    [imageView release];
    imageView = nil;
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

@end

#2

And I solved my own problem. I had somehow connected the ‘view’ outlet to my UIImageView object on the XIB, so both imageView and view were being directed there.

Code was good, it was the other stuff I cheesed up.

Cheers, and thanks for the great book!