Can't Enter TO DO Items


#1

I could run the program, wired all objects/ datasource But when I can enter items. My code is attached below.
Could you help me? Thanks

#import “BNRDocument.h”

@implementation BNRDocument

#pragma mark - NSDocument Overrides

  • (NSString *)windowNibName
    {
    return @“BNRDocument”;
    }

#pragma mark - Actions

  • (IBAction)createNewItem:(id)sender
    {
    if (!todoItems) {
    todoItems = [NSMutableArray array];
    }
    [todoItems addObject:@“New Item”];

    [itemTableView reloadData];
    [self updateChangeCount:NSChangeDone];
    }

#pragma mark Data Source Methods

  • (NSInteger)numberOfRowsInTableView:(NSTableView *)tv
    {
    return [todoItems count];
    }

  • (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
    {
    return [todoItems objectAtIndex:row];
    }

  • (void) tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
    {
    [todoItems replaceObjectAtIndex:row withObject:object];
    [self updateChangeCount:NSChangeDone];
    }

  • (id)init{
    self = [super init];
    if (self) {
    // NSArray *array = [NSArray arrayWithObjects:@“aa”, @“BB”, nil];
    // todoItems = [NSMutableArray arrayWithArray:array];
    }
    return self;
    }

  • (void)windowControllerDidLoadNib:(NSWindowController *)aController
    {
    [super windowControllerDidLoadNib:aController];
    // Add any code here that needs to be executed once the windowController has loaded the document’s window.
    }

  • (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
    {
    if (!todoItems) {
    todoItems = [NSMutableArray array];
    }
    NSData *data = [NSPropertyListSerialization dataWithPropertyList:todoItems format:NSPropertyListXMLFormat_v1_0 options:0 error];
    return data;
    }

  • (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
    {
    todoItems = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainers format:NULL error];
    return (todoItems != nil);
    }

@end


#2

I don’t have any suggestions, but could you provide more information about what happens? Any messages in the console, what behavior do you see when you’re interacting with the application?


#3

I have exactly the same problem my code is the same. the Window comes up, but you cannot enter anything. when you type a letter you get a beep. the debug says this:tty /dev/ttys000
[Switching to process 1492 thread 0x0]


#4

…me too…every thing seems to work fine…but I cant enter text…if I run the application and I try to write it just goes…“BUMP!”

…I can click the button thow, and “next item” is added to the vindow every time…

It’s as if I dindnt make a window where I could Write…

…any clues?


#5

OK, I had the same problem for several hours. Try this…

DOUBLE click the item in order to type over it. :laughing:

(Yes, I bought the Mac specifically to learn iOS programming. Mac OS noobs unite…)


#6

Thank you ErinMichelle! I was getting quite discouraged on this one and all I needed was a double click? Lol cheers.


#7

Hi Guys

My problem somehow seems to remain. Hence I can’t enter any Todo-Lines. Here’s my code, maybe someone can help me.

Kind regards

Serge

@implementation BNRAppDelegate

// Implements the addTask

  • (void)addTask:(id)sender
    {
    // Get the to-do item
    NSString *t = [taskField text];
    // Quit here if taskField is empty
    if ([t isEqualToString:@""]) {
    return;
    }
    // Add it to our working array
    [tasks addObject:t];
    // Refresh the table so that the new item shows up
    [taskTable reloadData];
    // And clear out the text field
    [taskField setText:@""];
    // Dismiss the keyboard
    [taskField resignFirstResponder];
    }

#pragma mark - Table View management

- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
    // Because this table view only has one section,
    // the number of rows in it is equal to the number
    // of items in our tasks array
    return [tasks count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView

cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// To improve performance, we reconfigure cells in memory
// that have scrolled off the screen and hand them back
// with new contents instead of always creating new cells.
// First, we check to see if there’s a cell available for reuse.

    UITableViewCell *c = [taskTable dequeueReusableCellWithIdentifier:@"Cell"];
    if (!c) {
        // ...and only allocate a new cell if none are available
        c = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                   reuseIdentifier:@"Cell"];
    }
    // Then we (re)configure the cell based on the model object,
    // in this case our todoItems array
    NSString *item = [tasks objectAtIndex:[indexPath row]];
    [[c textLabel] setText:item];
    // and hand back to the table view the properly configured cell
    return c;
}

#pragma mark - Application delegate callbacks

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    // Attempt to load an existing to-do dataset from an array stored to disk.
    NSArray *plist = [NSArray arrayWithContentsOfFile:docPath()];
    if (plist) {
    // If there was a dataset available, copy it into our instance variable.
    tasks = [plist mutableCopy];
    }
    else {
    // Otherwise, just create an empty one to get us started.
    tasks = [[NSMutableArray alloc] init];
    }

    // Is tasks empty?
    if ([tasks count] == 0) {
    }

// Create and configure the UIWindow instance
// A CGRect is a struct with an origin (x,y) and size (width,height)

CGRect windowFrame = [[UIScreen mainScreen] bounds];
UIWindow *theWindow = [[UIWindow alloc] initWithFrame:windowFrame];
[self setWindow:theWindow];

// Define the frame rectangles of the three UI elements
// CGRectMake() creates a CGRect from (x, y, width, height)

CGRect tableFrame = CGRectMake(0, 80, 320, 300);
CGRect fieldFrame = CGRectMake(20, 40, 200, 31);
CGRect buttonFrame = CGRectMake(228, 40, 72, 31);

// Create and configure the table view

taskTable = [[UITableView alloc] initWithFrame:tableFrame
                                         style:UITableViewStylePlain];
[taskTable setSeparatorStyle:UITableViewCellSeparatorStyleNone];

// Make this object the table view’s dataSource
[taskTable setDataSource:self];
// Create and configure the text field where new tasks will be typed
taskField = [[UITextField alloc] initWithFrame:fieldFrame];

// Create and configure the text field where new tasks will be typed

taskField = [[UITextField alloc] initWithFrame:fieldFrame];
[taskField setBorderStyle:UITextBorderStyleRoundedRect];
[taskField setPlaceholder:@“Type a task, tap Insert”];

// Create and configure a rounded rect Insert button
insertButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[insertButton setFrame:buttonFrame];

// Buttons behave using a target/action callback
// Configure the Insert button’s action to call this object’s -addTask: method

[insertButton addTarget:self
             action:@selector(addTask:)
   forControlEvents:UIControlEventTouchUpInside];

// Give the button a title
[insertButton setTitle:@"Insert"
forState:UIControlStateNormal];

// Add our three UI elements to the window
[[self window] addSubview:taskTable];
[[self window] addSubview:taskField];
[[self window] addSubview:insertButton];

// Finalize the window and put it on the screen
[[self window] setBackgroundColor:[UIColor lightGrayColor]];
[[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
    {
    // This method is only called in iOS 4.0+

    // 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.
    // Save our tasks array to disk
    [tasks writeToFile:docPath() atomically:YES];
    }

  • (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:.

    // Save our tasks array to disk
    [tasks writeToFile:docPath() atomically:YES];
    }

@end


#8

Your code is for an iOS application, but you are posting here which is for Chapter 28 - Your First Cocoa Application!

What are you trying to do exactly? Are you trying to create an iOS application or a Cocoa Application?

By the way, I have built and run your code on iOS 5; it works.

I am posting it back properly by using the code tags for your reference:

//  AppDelegate.m

#import "AppDelegate.h"

// --------------------------------------------------------------------------------
//
NSString *docPath()
{
    NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
    return [[pathList objectAtIndex:0] stringByAppendingPathComponent:@"data.td"];
}

// --------------------------------------------------------------------------------
//
@interface AppDelegate ()
{
    UITableView *taskTable;
    UITextField *taskField;
    UIButton *insertButton;
    
    NSMutableArray *tasks;
}
@end

// --------------------------------------------------------------------------------
//
@implementation AppDelegate

@synthesize window = _window;

// Implements the addTask

- (void)addTask:(id)sender
{
    // Get the to-do item
    NSString *t = [taskField text];
    // Quit here if taskField is empty
    if ([t isEqualToString:@""]) {
        return;
    }
    // Add it to our working array
    [tasks addObject:t];
    // Refresh the table so that the new item shows up
    [taskTable reloadData];
    // And clear out the text field
    [taskField setText:@""];
    // Dismiss the keyboard
    [taskField resignFirstResponder];
}


#pragma mark - Table View management

- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section
{
    // Because this table view only has one section,
    // the number of rows in it is equal to the number
    // of items in our tasks array
    return [tasks count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // To improve performance, we reconfigure cells in memory
    // that have scrolled off the screen and hand them back
    // with new contents instead of always creating new cells.
    // First, we check to see if there's a cell available for reuse.
    
    UITableViewCell *c = [taskTable dequeueReusableCellWithIdentifier:@"Cell"];
    if (!c) {
        // ...and only allocate a new cell if none are available
        c = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                   reuseIdentifier:@"Cell"];
    }
    // Then we (re)configure the cell based on the model object,
    // in this case our todoItems array
    NSString *item = [tasks objectAtIndex:[indexPath row]];
    [[c textLabel] setText:item];
    // and hand back to the table view the properly configured cell
    return c;
}

// --------------------------------------------------------------------------------
//
#pragma mark - Application delegate callbacks

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Attempt to load an existing to-do dataset from an array stored to disk.
    NSArray *plist = [NSArray arrayWithContentsOfFile:docPath()];
    if (plist) {
        // If there was a dataset available, copy it into our instance variable.
        tasks = [plist mutableCopy];
    }
    else {
        // Otherwise, just create an empty one to get us started.
        tasks = [[NSMutableArray alloc] init];
    }
    
    // Is tasks empty?
    if ([tasks count] == 0) {
    }
    
    // Create and configure the UIWindow instance
    // A CGRect is a struct with an origin (x,y) and size (width,height)
    
    
    CGRect windowFrame = [[UIScreen mainScreen] bounds];
    UIWindow *theWindow = [[UIWindow alloc] initWithFrame:windowFrame];
    [self setWindow:theWindow];
    
    // Define the frame rectangles of the three UI elements
    // CGRectMake() creates a CGRect from (x, y, width, height)
    
    CGRect tableFrame = CGRectMake(0, 80, 320, 300);
    CGRect fieldFrame = CGRectMake(20, 40, 200, 31);
    CGRect buttonFrame = CGRectMake(228, 40, 72, 31);
    
    
    // Create and configure the table view
    
    taskTable = [[UITableView alloc] initWithFrame:tableFrame
                                             style:UITableViewStylePlain];
    [taskTable setSeparatorStyle:UITableViewCellSeparatorStyleNone];
    
    
    // Make this object the table view's dataSource
    [taskTable setDataSource:self];
    // Create and configure the text field where new tasks will be typed
    taskField = [[UITextField alloc] initWithFrame:fieldFrame];
    
    
    // Create and configure the text field where new tasks will be typed
    
    taskField = [[UITextField alloc] initWithFrame:fieldFrame];
    [taskField setBorderStyle:UITextBorderStyleRoundedRect];
    [taskField setPlaceholder:@"Type a task, tap Insert"];
    
    // Create and configure a rounded rect Insert button
    insertButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [insertButton setFrame:buttonFrame];
    
    // Buttons behave using a target/action callback
    // Configure the Insert button's action to call this object's -addTask: method
    
    [insertButton addTarget:self
                     action:@selector(addTask:)
           forControlEvents:UIControlEventTouchUpInside];
    
    
    // Give the button a title
    [insertButton setTitle:@"Insert"
                  forState:UIControlStateNormal];
    
    // Add our three UI elements to the window
    [[self window] addSubview:taskTable];
    [[self window] addSubview:taskField];
    [[self window] addSubview:insertButton];
    
    // Finalize the window and put it on the screen
    [[self window] setBackgroundColor:[UIColor lightGrayColor]];
    [[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
{
    // This method is only called in iOS 4.0+
    
    // 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.
    // Save our tasks array to disk
    [tasks writeToFile:docPath() atomically:YES];
}

- (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:.
    
    // Save our tasks array to disk
    [tasks writeToFile:docPath() atomically:YES]; 
}

@end

To build it, create an iOS project for an Empty Application and replace the AppDelegate’s code with the code above.


#9

Great, my first question, and I’m copying the wrong code. :blush:
My question was in fact about the first Cocoa Application, not the iOS-Code for the iTahDoodle, which I posted.

Sorry about that.

The iTahDoodle runs perfectly well, it’s the TahDoodle app which causes a problem. I’ve posted the code from the BNRDocument.m file, as I’ve transcribed from the book.
There are no compiling issues according to XCode, but when I run the app I can’t enter any todo-items. Maybe I’ve missed something.

Thanks again for the help.

[code]//
// BNRDocument.m
// TahDoodle
//
// Created by Serge Zehnder on 21.01.13.
// Copyright © 2013 Serge Zehnder. All rights reserved.
//

#import “BNRDocument.h”

@implementation BNRDocument

  • (id)init
    {
    self = [super init];
    if (self) {
    // Add your subclass-specific initialization here.
    }
    return self;
    }

#pragma mark - NSDocument Overrides

  • (NSString *)windowNibName
    {
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @“BNRDocument”;
    }

#pragma mark - Actions

  • (IBAction)createNewItem:(id)sender
    {
    // If there’s no array yet, go ahead and create one to store our new task
    if (!todoItems)
    {
    todoItems = [NSMutableArray array];
    }
    [todoItems addObject:@“New Item”];
    // -reloadData tells the table view to refresh and ask its dataSource
    // (which happens to be this BNRDocument object in this case)
    // for new data to display
    [itemTableView reloadData];

    // -updateChangeCount: tells the application whether or not the document
    // has unsaved changes. NSChangeDone flags the document as unsaved.
    [self updateChangeCount:NSChangeDone];
    }

#pragma mark Data Source Methods

  • (NSInteger)numberOfRowsInTableView:(NSTableView *)tv
    {
    // This table view is meant to display the todoItems,
    // so the number of entries in the table view will be the same
    // as the number of objects in the array.
    return [todoItems count];
    }

  • (id)tableView:(NSTableView *)tableView
    objectValueForTableColumn:(NSTableColumn *)tableColumn
    row:(NSInteger)row
    {
    // Return the item from todoItems that corresponds to the cell
    // that the table view wants to display
    return [todoItems objectAtIndex:row];
    }

  • (void)tableView:(NSTableView *)tableView
    setObjectValue:(id)object
    forTableColumn:(NSTableColumn *)tableColumn
    row:(NSInteger)row
    {
    // When the user changes a to-do item on the table view,
    // update the todoItems array
    [todoItems replaceObjectAtIndex:row withObject:object];
    // And then flag the document as having unsaved changes.
    [self updateChangeCount:NSChangeDone];
    }

  • (void)windowControllerDidLoadNib:(NSWindowController *)aController
    {
    [super windowControllerDidLoadNib:aController];
    // Add any code here that needs to be executed once the windowController has loaded the document’s window.
    }

  • (BOOL)autosavesInPlace
    {
    return YES;
    }
  • (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
    {
    // This method is called when our document is being saved
    // We are expected to hand the caller an NSData object wrapping our data // so that it can be written to disk
    // If there’s no array, we’ll write out an empty array for now if (!todoItems)
    {
    todoItems = [NSMutableArray array];
    }
    // Pack our todoItems array into an NSData object

    NSData *data = [NSPropertyListSerialization dataWithPropertyList:todoItems format:NSPropertyListXMLFormat_v1_0 options:NSPropertyListMutableContainers error];
    // return our newly-packed NSData object
    return data; }

  • (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
    {
    // This method is called when a document is being loaded
    // We are handed an NSData object and expected to pull our data out of it
    // Extract our todoItems

    todoItems = [NSPropertyListSerialization propertyListWithData:data options:0
    format:NULL
    error];
    // return success or failure depending on success of the above call
    return (todoItems != nil);
    }

@end
[/code]


#10

Okay, we are in synch now.

Does the createNewItem: method get called at all?

Add an NSLog statement to see if this is happening:

- (IBAction)createNewItem:(id)sender
{
   NSLog (@"--->%s", __PRETTY_FUNCTION__);
   ...
}

#11

So far the TahDoodle-App properly starts and when I click on the textarea Xcode gives me the following output:
2013-01-24 14:07:48.038 TahDoodle[19240:303] —>-[BNRDocument createNewItem:] and in the app itself just adds a row of info called “Table View Cell”.

So it seems the that the method is called, but I’m unable to type anything into the actual text field.


#12

Have you tried double-clicking the cell to edit it in the table view?


#13

Yes, I’ve done the double-clicking but all that happens that more rows are created.

This is the output from Xcode.

2013-01-25 13:36:27.883 TahDoodle[39149:303] —>-[BNRDocument createNewItem:]
2013-01-25 13:36:28.941 TahDoodle[39149:303] —>-[BNRDocument createNewItem:]
2013-01-25 13:36:29.135 TahDoodle[39149:303] —>-[BNRDocument createNewItem:]


#14

Make sure that all method names are spelled correctly, paying special attention to the case of the letters. Also make sure that the table column is editable in Interface Builder,

If it still doesn’t work, please post your code BNRDocument.m between the Code tags.


#15

Hi,

I’ve double-checked my BNRDocument.m twice and I’ve entered the code according to the book.

[code]//
// BNRDocument.m
// TahDoodle
//
// Created by Serge Zehnder on 21.01.13.
// Copyright © 2013 Serge Zehnder. All rights reserved.
//

#import “BNRDocument.h”

@implementation BNRDocument

#pragma mark - NSDocument Overrides

  • (NSString *)windowNibName
    {
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @“BNRDocument”;
    }

#pragma mark - Actions

  • (IBAction)createNewItem:(id)sender
    {
    // If there’s no array yet, go ahead and create one to store our new task
    if (!todoItems)
    {
    todoItems = [NSMutableArray array];
    }

    [todoItems addObject:@“New Item”];

    // -reloadData tells the table view to refresh and ask its dataSource
    // (which happens to be this BNRDocument object in this case)
    // for new data to display
    [itemTableView reloadData];

    // -updateChangeCount: tells the application whether or not the document
    // has unsaved changes. NSChangeDone flags the document as unsaved.
    [self updateChangeCount:NSChangeDone];
    }

#pragma mark Data Source Methods

  • (NSInteger)numberOfRowsInTableView:(NSTableView *)tv
    {
    // This table view is meant to display the todoItems,
    // so the number of entries in the table view will be the same
    // as the number of objects in the array.
    return [todoItems count];
    }

  • (id)tableView:(NSTableView *)tableView
    objectValueForTableColumn:(NSTableColumn *)tableColumn
    row:(NSInteger)row
    {
    // Return the item from todoItems that corresponds to the cell
    // that the table view wants to display

    return [todoItems objectAtIndex:row];
    }

  • (void)tableView:(NSTableView *)tableView
    setObjectValue:(id)object
    forTableColumn:(NSTableColumn *)tableColumn
    row:(NSInteger)row
    {
    // When the user changes a to-do item on the table view,
    // update the todoItems array
    [todoItems replaceObjectAtIndex:row withObject:object];
    // And then flag the document as having unsaved changes.
    [self updateChangeCount:NSChangeDone];
    }

  • (NSData *)dataOfType:
    (NSString *)typeName
    error:(NSError **)outError
    {
    // This method is called when our document is being saved
    // We are expected to hand the caller an NSData object wrapping our data
    // so that it can be written to disk
    // If there’s no array, we’ll write out an empty array for now

    if (!todoItems)
    {
    todoItems = [NSMutableArray array];
    }

// Pack our todoItems array into an NSData object

NSData *data = [NSPropertyListSerialization dataWithPropertyList:todoItems
                                                          format:NSPropertyListXMLFormat_v1_0 options:NSPropertyListMutableContainers error];

// return our newly-packed NSData object
return data;

}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError

{
// This method is called when a document is being loaded
// We are handed an NSData object and expected to pull our data out of it
// Extract our todoItems

todoItems = [NSPropertyListSerialization propertyListWithData:data
                                                      options:0
                                                       format:NULL
                                                        error];
// return success or failure depending on success of the above call
return
(todoItems != nil);

}
@end
[/code]

Unfortunately the problem persists, I’m still not able to enter any kind of todo items, all I get is the “Table View Cell” output in TahDoodle.


#16

I have used your code, which works perfectly, but added some logging statements to it:

//
//  BNRDocument.m
//  TahDoodle
//
//  Created by Serge Zehnder on 21.01.13.
//  Copyright (c) 2013 Serge Zehnder. All rights reserved.
//

#import "BNRDocument.h"

@implementation BNRDocument

#pragma mark - NSDocument Overrides 

- (NSString *)windowNibName
{
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @"BNRDocument";
}

#pragma mark - Actions

- (IBAction)createNewItem:(id)sender
{
    NSLog (@"---> %s", __func__);
    
    // If there's no array yet, go ahead and create one to store our new task
    if (!todoItems)
    {
        todoItems = [NSMutableArray array];
    }
    
    [todoItems addObject:@"New Item"];
    
    // -reloadData tells the table view to refresh and ask its dataSource
    // (which happens to be this BNRDocument object in this case)
    // for new data to display
    [itemTableView reloadData];
    
    // -updateChangeCount: tells the application whether or not the document
    // has unsaved changes. NSChangeDone flags the document as unsaved.
    [self updateChangeCount:NSChangeDone];
}

#pragma mark Data Source Methods

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv
{
    NSLog (@"---> %s", __func__);

    // This table view is meant to display the todoItems,
    // so the number of entries in the table view will be the same
    // as the number of objects in the array.
    return [todoItems count];
}

- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
            row:(NSInteger)row
{
    NSLog (@"---> %s", __func__);

    // Return the item from todoItems that corresponds to the cell
    // that the table view wants to display
    
    return [todoItems objectAtIndex:row];
}

- (void)tableView:(NSTableView *)tableView
   setObjectValue:(id)object
   forTableColumn:(NSTableColumn *)tableColumn
              row:(NSInteger)row
{
    NSLog (@"---> %s: %@", __func__, object);

    // When the user changes a to-do item on the table view,
    // update the todoItems array
    [todoItems replaceObjectAtIndex:row withObject:object];
    // And then flag the document as having unsaved changes.
    [self updateChangeCount:NSChangeDone];
}

- (NSData *)dataOfType:
(NSString *)typeName
                 error:(NSError **)outError
{
    // This method is called when our document is being saved
    // We are expected to hand the caller an NSData object wrapping our data
    // so that it can be written to disk
    // If there's no array, we'll write out an empty array for now
    
    if (!todoItems)
    {
        todoItems = [NSMutableArray array];
    }
    
    // Pack our todoItems array into an NSData object
    
    NSData *data = [NSPropertyListSerialization dataWithPropertyList:todoItems
                                                              format:NSPropertyListXMLFormat_v1_0 options:NSPropertyListMutableContainers error];
    // return our newly-packed NSData object
    return data;
    
}


- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
    // This method is called when a document is being loaded
    // We are handed an NSData object and expected to pull our data out of it
    // Extract our todoItems
    
    todoItems = [NSPropertyListSerialization propertyListWithData:data
                                                          options:0
                                                           format:NULL
                                                            error];
    // return success or failure depending on success of the above call
    return
    (todoItems != nil);
}
@end

I was also able to rename each row with the default task name New Item.

And it produced this output:

2013-01-27 11:45:57.930 SergeZ28[29012:403] ---> -[BNRDocument numberOfRowsInTableView:]
2013-01-27 11:46:00.892 SergeZ28[29012:403] ---> -[BNRDocument createNewItem:]
2013-01-27 11:46:00.893 SergeZ28[29012:403] ---> -[BNRDocument numberOfRowsInTableView:]
2013-01-27 11:46:00.912 SergeZ28[29012:403] ---> -[BNRDocument tableView:objectValueForTableColumn:row:]
...
2013-01-27 11:46:19.682 SergeZ28[29012:403] ---> -[BNRDocument tableView:setObjectValue:forTableColumn:row:]: Task 1
2013-01-27 11:46:19.685 SergeZ28[29012:403] ---> -[BNRDocument tableView:objectValueForTableColumn:row:]
2013-01-27 11:46:22.018 SergeZ28[29012:403] ---> -[BNRDocument createNewItem:]
2013-01-27 11:46:22.019 SergeZ28[29012:403] ---> -[BNRDocument numberOfRowsInTableView:]
2013-01-27 11:46:22.038 SergeZ28[29012:403] ---> -[BNRDocument tableView:objectValueForTableColumn:row:]

2013-01-27 11:46:31.302 SergeZ28[29012:403] ---> -[BNRDocument createNewItem:]
2013-01-27 11:46:31.304 SergeZ28[29012:403] ---> -[BNRDocument tableView:objectValueForTableColumn:row:]
2013-01-27 11:46:31.305 SergeZ28[29012:403] ---> -[BNRDocument tableView:setObjectValue:forTableColumn:row:]: Task 2
2013-01-27 11:46:31.305 SergeZ28[29012:403] ---> -[BNRDocument numberOfRowsInTableView:]
2013-01-27 11:46:31.320 SergeZ28[29012:403] --->-[BNRDocument tableView:objectValueForTableColumn:row:]
...

Then, most likely cause: you are not doing something right when configuring the controls in the NIB file.

In the BNRDocument.xib NIB file, check to make sure that Table View’s Content Mode attribute is Cell Based not [color=#FF0000]View Based[/color].


#17

I see this issue too. How did you guys fix this issue? Thanks.
When I ran the code I get a a window pop up with the text cell and insert button I created, but I can’t type anything into the text cell nor use the insert button.
If I press insert button, then I see this error.
Thread 1: EXC_BAD_ACCESS(Code=13, address=0x0)
2013-01-27 12:29:08.283 TahDoodle[3398:303] —>-[BNRDocument createNewItem:]
(lldb)

Here’s my code:

//
//  BNRDocument.m
//  TahDoodle
//
//  Created by vnvdm on 1/27/13.
//  Copyright (c) 2013 vnvdm. All rights reserved.
//

#import "BNRDocument.h"

@implementation BNRDocument





- (IBAction)createNewItem:(id)sender
{
       NSLog (@"--->%s", __PRETTY_FUNCTION__);
    
    //If there's no array yet, go ahead and create one to store our new task
    if (!todoItems){
        todoItems = [NSMutableArray array];
    }
    [todoItems addObject:@"New Item"];
    
    //-reloadData tells the table view to refresh and ask its dataSource
    //(which happens to be this BNRDocument object in this case)
    //for new data to display
    [itemTableView reloadData];
    
    //-updateChangeCount: tells the applicatin whether or not the document
    //has unsaved changes. NSChangeDone flags the document as unsaved.
    [self updateChangeCount:NSChangeDone];
}


- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv
{
    //This table view is meant to display the todoItems,
    //so the number of entries in the table view will be the same
    //as the number of objects in the array.
    return [todoItems count];
}


- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    //Return the item from todoItems that corresponds to the cell
    //that the table view wants to display
    return [todoItems objectAtIndex:row];
}


- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row

{
    //When the user changes a to-do item on the table view,
    //update the todoItems array
    [todoItems replaceObjectAtIndex:row withObject:object];
    //And then flag the document as having unsaved changes.
    [self updateChangeCount:NSChangeDone];
}

- (id)init
{
    self = [super init];
    if (self) {
        // Add your subclass-specific initialization here.
    }
    return self;
}

#pragma mark - NSDocument Overrides

- (NSString *)windowNibName
{
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @"BNRDocument";
}

- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
    [super windowControllerDidLoadNib:aController];
    // Add any code here that needs to be executed once the windowController has loaded the document's window.
}

+ (BOOL)autosavesInPlace
{
    return YES;
}

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
   
    
    //This method is called when our document is being saved
    //We are expected to hand the caller an NSData object wrapping our data
    //so that it can be written to disk
    
    //If there's no array, we'll write out an empty array for now
    
    if (!todoItems) {
        todoItems = [NSMutableArray array];
    }
    
    //Pack our todoItems array into an NSData object
    NSData *data = [NSPropertyListSerialization dataWithPropertyList:todoItems format:NSPropertyListXMLFormat_v1_0 options:0 error];
    
    //return our newly packed NSData object
    return data;
    
    // Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil.
    // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
   // NSException *exception = [NSException exceptionWithName:@"UnimplementedMethod" reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
   // @throw exception;
    //return nil;
}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
    
    //This method is called when a document is being loaded
    //We are handed an NSData object and expected to pull our data out of it
    
    //Extract our todoItems
    todoItems = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainers format:NULL error];
    
    //return success or failure depending on success of the above call
    return (todoItems != nil);
    
    // Insert code here to read your document from the given data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning NO.
    // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.
    // If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded.
   // NSException *exception = [NSException exceptionWithName:@"UnimplementedMethod" reason:[NSString stringWithFormat:@"%@ is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
   // @throw exception;
   // return YES;
}

@end

#import <Cocoa/Cocoa.h>

@interface BNRDocument : NSDocument <NSTableViewDataSource>
{
    NSMutableArray *todoItems;
    IBOutlet NSTableView *itemTableView;
}

-(IBAction)createNewItem:(id)sender;


@end

#18

Thanks for your help. I’m now able to enter things into my todo-list.

On the other hand I’ve now got the problem that everytime I click on the “TahDoodle”-App a new item is being added to the list before I’m even able to properly define the new entry. Meaning, I click on the “TahDoodle” an “New Item” appears, then I double click on this “New item” in order to specify the entry with something like “Buy Milk”, and two new “New Items” appear within the app.


#19

Where do I find the Content Mode attribute for Table View?

I’m using Xcode v3.2.6 and there is no Content Mode attribute??

Is there a workaround? I’m still unable to add an item in Table View.

Thanks!


#20

Using Xcode 5, and yes, I had to change my view from Cell based and NOT View base. Works like a charm! :smiley: Thank You!!!