I get the strangest error


#1

I have just connected DetailViewController with the cells in ItemsViewController. When I am running the simulator though, only half of the cells push an instance of DetailViewController on the view.

The first cell I create won’t do anything, the second works properly, the third not, the fourth works and so on. I.e. only every second cell works. I post my code below, and hope that anyone could help me. I would be much thankful!

BNRItem.h

[code]#import <Foundation/Foundation.h>

@interface BNRItem : NSObject

  • (id)randomItem;
  • (id)initWithItemName:(NSString *)name
    valueInDollars:(int)value
    serialNumber:(NSString *)sNumber;

@property (nonatomic, strong) BNRItem *containedItem;
@property (nonatomic, weak) BNRItem *container;

@property (nonatomic, copy) NSString *itemName;
@property (nonatomic, copy) NSString *serialNumber;
@property (nonatomic) int valueInDollars;
@property (nonatomic, readonly, strong) NSDate *dateCreated;

@end[/code]

BNRItem.m

[code]#import “BNRItem.h”

@implementation BNRItem

  • (id)randomItem
    {
    // Create an array of three adjectives
    NSArray *randomAdjectiveList = [NSArray arrayWithObjects:@“Fluffig”,
    @“Rostig”,
    @“Skinande”, nil];

    // Create an array of three nouns
    NSArray *randomNounList = [NSArray arrayWithObjects:@“björn”,
    @“gaffel”,
    @“mobil”, nil];

    // Get the index of a random adjective/noun from the lists
    // Note: the % operator, called the modulo operator, gives you the remainder.
    // So adjectiveIndex is a random number from 0 to 2.
    NSInteger adjectiveIndex = rand() % [randomAdjectiveList count];
    NSInteger nounIndex = rand() % [randomNounList count];

    // Note that NSInteger is not an object, but a type definition for “unsigned long”

    NSString *randomName = [NSString stringWithFormat:@"%@ %@",
    [randomAdjectiveList objectAtIndex:adjectiveIndex],
    [randomNounList objectAtIndex:nounIndex]];

    int randomValue = rand() % 100;

    NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c",
    ‘0’ + rand() % 10,
    ‘A’ + rand() % 26,
    ‘0’ + rand() % 10,
    ‘A’ + rand() % 26,
    ‘0’ + rand() % 10];

    BNRItem *newItem = [[self alloc] initWithItemName:randomName
    valueInDollars:randomValue
    serialNumber:randomSerialNumber];

    return newItem;
    }

#pragma mark - class specific methods

// designated initializer

  • (id)initWithItemName:(NSString *)name
    valueInDollars:(int)value
    serialNumber:(NSString *)sNumber
    {
    // Call the superclass’s designated initializer
    self = [super init];

    //Did the superclass’s designated initializer succeed?
    if (self) {
    // Give the instance variables initital values
    [self setItemName:name];
    [self setSerialNumber:sNumber];
    [self setValueInDollars:value];
    dateCreated = [[NSDate alloc] init];
    }

    // Return the address of the newly initialized object
    return self;
    }

@synthesize itemName, containedItem, container, serialNumber, valueInDollars, dateCreated;

  • (void)setContainedItem:(BNRItem *)i;
    {
    containedItem = i;
    [i setContainer:self];
    }

#pragma mark - overridden methods

  • (NSString *)description
    {
    NSString *descriptionString = [[NSString alloc] initWithFormat:@"%@ (%@): Värde $%d, skapad %@",
    itemName,
    serialNumber,
    valueInDollars,
    dateCreated];
    return descriptionString;
    }

  • (id)init
    {
    return [self initWithItemName:@“Item"
    valueInDollars:0
    serialNumber:@”"];
    }

  • (void)dealloc
    {
    NSLog(@“Destroyed: %@”, self);
    }

@end
[/code]

BNRItemStore.h

#import <Foundation/Foundation.h>

@class BNRItem;

@interface BNRItemStore : NSObject
{
    NSMutableArray *allItems;
}


// Notice that this is a class method and prefixed with a + instead of a -
+ (BNRItemStore *)sharedStore;


- (NSArray *)allItems;
- (BNRItem *)createItem;
- (void)removeItem:(BNRItem *)p;
- (void)moveItemAtIndex:(int)from
                toIndex:(int)to;

@end

BNRItemStore.m

[code]
#import “BNRItemStore.h”
#import “BNRItem.h”

@implementation BNRItemStore

  • (BNRItemStore *)sharedStore
    {
    static BNRItemStore *sharedStore = nil;
    if (!sharedStore)
    sharedStore = [[super allocWithZone:nil] init];

    return sharedStore;
    }

  • (NSArray *)allItems
    {
    return allItems;
    }

  • (BNRItem *)createItem
    {
    BNRItem *p = [BNRItem randomItem];

    [allItems addObject:p];

    return p;
    }

  • (void)removeItem:(BNRItem *)p
    {
    [allItems removeObjectIdenticalTo:p];
    }

  • (void)moveItemAtIndex:(int)from
    toIndex:(int)to
    {
    if (from == to) {
    return;
    }

    // Get a pointer to object being moced so we can re-insert it
    BNRItem *p = [allItems objectAtIndex:from];

    // Remove p from array
    [allItems removeObjectAtIndex:from];

    // Insert p in array at new location
    [allItems insertObject:p
    atIndex:to];

}

#pragma mark - Overriden methods

  • (id)allocWithZone:(NSZone *)zone
    {
    return [self sharedStore];
    }
  • (id)init
    {
    self = [super init];
    if (self) {
    allItems = [[NSMutableArray alloc] init];
    }
    return self;
    }

@end[/code]

ItemsViewController.h

[code]
#import <Foundation/Foundation.h>
#import “DetailViewController.h”

@interface ItemsViewController : UITableViewController
{
IBOutlet UIView *headerView;
}

  • (UIView *)headerView;
  • (IBAction)addNewItem:(id)sender;
  • (IBAction)toggleEditingMode:(id)sender;

@end[/code]

ItemsViewController.m

[code]
#import “ItemsViewController.h”
#import “BNRItemStore.h”
#import “BNRItem.h”

@implementation ItemsViewController

{}
#pragma mark - initializers

  • (id)init
    {
    // Call the superclass’s designated initializer
    self = [super initWithStyle:UITableViewStyleGrouped];
    if (self) {

    }
    return self;
    }

// Ensures that all instances are creates in grouped style, no matter what init message is sent

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

#pragma mark - class specific methods

  • (UIView *)headerView
    {
    // If we haven’t loaded the headerView yet…
    if (!headerView) {
    // Load HeaderView.xib
    [[NSBundle mainBundle] loadNibNamed:@"HeaderView"
    owner:self
    options:nil];
    }
    return headerView;
    }

  • (IBAction)toggleEditingMode:(id)sender
    {
    // If we are currently in editing mode…
    if([self isEditing]) {
    // Change text of button to inform user of state
    [sender setTitle:@"Ändra"
    forState:UIControlStateNormal];
    // Turn off editing mode
    [self setEditing:NO
    animated:YES];
    } else {
    // Change text of button to inform user of state
    [sender setTitle:@"Klar"
    forState:UIControlStateNormal];
    // Enter editing mode
    [self setEditing:YES
    animated:YES];
    }
    }

  • (IBAction)addNewItem:(id)sender
    {
    // Create a new BNRItem and add it to the store
    BNRItem *newItem = [[BNRItemStore sharedStore] createItem];

    // Figure out where that item is in the array
    int lastRow = [[[BNRItemStore sharedStore] allItems] indexOfObject:newItem];

    NSIndexPath *ip = [NSIndexPath indexPathForRow:lastRow
    inSection:0];

    // Insert this new row into the table
    [[self tableView] insertRowsAtIndexPaths:[NSArray arrayWithObject:ip]
    withRowAnimation:UITableViewRowAnimationTop];
    }

#pragma mark - TVDataSource protocol methods

  • (NSInteger)tableView:(UITableView *)tableView
    numberOfRowsInSection:(NSInteger)section
    {
    return [[[BNRItemStore sharedStore] allItems] count];
    }

  • (UITableViewCell *)tableView:(UITableView *)tableView
    cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    // Check for a reusable cell first, use that if it exists
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@“UITableViewCell”];

    // If there is no reusable cell of this type, create a new one
    if (!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
    reuseIdentifier:@“UITableViewCell”];
    }

    // Set the text on the cell with the description of the item that is at the n:th index of items,
    // where n = row, this cell will appear in on the tableview
    BNRItem *p = [[[BNRItemStore sharedStore] allItems] objectAtIndex:[indexPath row]];

    [[cell textLabel] setText:[p description]];

    return cell;
    }

  • (void)tableView:(UITableView *)tableView
    commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
    forRowAtIndexPath:(NSIndexPath *)indexPath
    {
    // If the table view is asking to commit a delete command…
    if (editingStyle == UITableViewCellEditingStyleDelete) {
    BNRItemStore *ps = [BNRItemStore sharedStore];
    NSArray *items = [ps allItems];
    BNRItem *p = [items objectAtIndex:[indexPath row]];
    [ps removeItem:p];

     // We also remove that row from the table view with an animation
     [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                      withRowAnimation:UITableViewRowAnimationFade];
    

    }
    }

  • (void)tableView:(UITableView *)tableView
    moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath
    toIndexPath:(NSIndexPath *)destinationIndexPath
    {
    [[BNRItemStore sharedStore] moveItemAtIndex:[sourceIndexPath row]
    toIndex:[destinationIndexPath row]];
    }

#pragma mark - TVDelegate protocol methods

  • (UIView *)tableView:(UITableView *)tv
    viewForHeaderInSection:(NSInteger)sec
    {
    return [self headerView];
    }

    • (CGFloat)tableView:(UITableView *)tv
      heightForHeaderInSection:(NSInteger)sec
      {
      // The height of the header view should be determined from the height of the view in the XIB file
      return [[self headerView] bounds].size.height;
      }

                      - (NSString *)tableView:(UITableView *)tableView
      

titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
return @“Stick och brinn!!”;
}

    - (void)tableView:(UITableView *)atableView

didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController *detailViewController = [[DetailViewController alloc] init];

// Push it onto the top of the navigation controller's stack
[[self navigationController] pushViewController:detailViewController
                                       animated:YES];

}

@end[/code]

DetailViewController.h

[code]#import <UIKit/UIKit.h>

@interface DetailViewController : UIViewController
{
__weak IBOutlet UITextField *nameField;
__weak IBOutlet UITextField *serialNumberField;
__weak IBOutlet UITextField *valueField;

__weak IBOutlet UILabel *dateLabel;

}
@end[/code]

DetailViewController.m

[code]#import “DetailViewController.h”

@interface DetailViewController ()

@end

@implementation DetailViewController

{}
#pragma mark - Overriden methods

  • (void)viewDidLoad
    {
    [super viewDidLoad];
    [[self view] setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
    }

@end[/code]

HomepwenrAppDelegate.h

[code]#import <UIKit/UIKit.h>

@interface HomepwnerAppDelegate : UIResponder

@property (strong, nonatomic) UIWindow *window;

@end[/code]

HomepwenrAppDelegate.m

[code]
#import “HomepwnerAppDelegate.h”
#import “ItemsViewController.h”

@implementation HomepwnerAppDelegate

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.

    // Create a ItemsViewController
    ItemsViewController *itemsViewController = [[ItemsViewController alloc] init];

    // Create an instance of a UINavigationController. Its stack contains only itemViewController.
    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:itemsViewController];

    // Place navigation controller’s virew in the window hierarchy
    [[self window] setRootViewController:navController];

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
    }

  • (void)applicationWillResignActive:(UIApplication *)application
    {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }

  • (void)applicationDidEnterBackground:(UIApplication *)application
    {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

  • (void)applicationWillEnterForeground:(UIApplication *)application
    {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    }

  • (void)applicationDidBecomeActive:(UIApplication *)application
    {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

  • (void)applicationWillTerminate:(UIApplication *)application
    {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }

@end[/code]


#2

You have made the same mistake most of us have probably made at some point.

In ItemsViewController you have implemented tableView:didDeselectRowAtIndexPath:, whereas you should have implemented tableView:didSelectRowAtIndexPath:

Nick
http://myfirstiphoneapp.co.uk


#3

Would not have found that error on my own. Thank you very much, Nick!