Help with Hillegasse book challenge- toDo app [NSTableView and NSMutableArray]

Discussion in 'Mac Programming' started by I'm a Mac, Jan 1, 2009.

  1. I'm a Mac macrumors 6502

    Joined:
    Nov 5, 2007
    #1
    I'm a little stuck on one of Hillegasses' challenges. The challenge is to make a ToDo app with NSTableView where you add an event from a textfield and extra points if it's editable. However, I can't get the events to add

    Here's my appcontroller.m
    Code:
    
    #import "AppController.h"
    
    
    @implementation AppController
    -(id) init
    {
    	[super init];
    	toDoList = [[NSMutableArray alloc] init];
    }
    
    -(int)numberOfRowsInTableView:(NSTableView *)tv
    {
    	return [toDoList count];
    }
    
    -(id)tableView:(NSTableView *)tv
    	objectValueForTableColumn:(NSTableColumn *)tableColumn
    		   row:(int)row {
    	
    NSString *toDo =  [toDoList objectAtIndex:row];
    	return toDo;
    	
    }
    
    -(void)tableViewSelectionDidChange:(NSNotification *)notification
    {
    	int row = [toDoTableView selectedRow];
    	if (row == -1) {
    		return;
    	}
    	NSString *selectedItem = [toDoList objectAtIndex:row];
    	NSLog (@"new thing to do = %@", selectedItem);
    
    }
    
    -(IBAction)createNewItem:(id) sender
    {
    	NSString *item = [newItemField stringValue];
    	if ([item length] == 0) {
    		return;
    	}
    		NSInteger row = [toDoTableView selectedRow];
    	if (row == -1){
    		return;
    	}
    	{[toDoList insertObject:item atIndex:row];
    		
    	}
    	{NSLog(@" have added %@ to list", item);
    		
    	}
    		
    	}
    
    At one point it would log that I added the events but still it wouldn't show, now, for some reason it doesn't do that anymore. I really appreciate you help.
    Also, I've tried the addOject method but that doesn't work either.
     
  2. turner296 macrumors newbie

    Joined:
    Jan 1, 2009
    #2
    You must tell the NSTableView to reload it's data. This syncs the dataSource with the NSTableView for newly added or removed data.

    Code:
    [toDoTableView reloadData];
    Put that in after the NSLog() function when you added your new todo item.

    Hope it works, took me a while getting my head around TableViews.
     
  3. I'm a Mac thread starter macrumors 6502

    Joined:
    Nov 5, 2007
    #3
    EDIT: thanks, I got it to work. I changed it back to addObject and deleted some unecessary { characters.

    EDIT #2: Okay, so I tried to make this editable, with some success. So when the user selects an item, it replaces that one (like it should but makes a duplicate, so now there are two of the newly added(changed) items

    Code:
    -(IBAction)createNewItem:(id) sender
    {
    	NSString *item = [newItemField stringValue];
    	if ([item length] == 0) {
    		return;
    	}
    	[toDoList addObject:item];
    		NSLog(@" have added %@ to list", item);
    		[toDoTableView reloadData];
    	NSInteger row = [toDoTableView selectedRow];
    	if (row == -1) {
    		return;
    	}
    	[toDoList replaceObjectAtIndex:row withObject:item];
    	NSLog(@"have changed event to %@", item);
    	[toDoTableView reloadData];
    	}
    
    Also, I get a warning in my init method that the "control reaches end of non-void function. I have no idea as to what that means, but I do know that it is good practice not to ignore warnings- any ideas as to what I should do?

    here's my init method
    Code:
    {
    	[super init];
    	toDoList = [[NSMutableArray alloc] init];
    }
    
     
  4. johnjay1776 macrumors member

    Joined:
    Jul 25, 2008
    #4
    You and I are about in the same place in that Hillegass book. I was able to get this to work OK. Would you like to see the code I wrote or would you prefer for me give you hints on how I was able to get it to work?
     
  5. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #5
    The line above reads like:

    -(id) init

    so you are returning an object of type id, but this isn't really specified in the init method above. I would try adding this:

    return self;

    This way the method returns an object of type AppController, which is also an object of type id.

    I would also suggest not using id unless you really needed to. Try specifying the object to be returned with

    -(AppController *) init
     
  6. I'm a Mac thread starter macrumors 6502

    Joined:
    Nov 5, 2007
    #6
    thanks for your help everyone- and yes- return self worked. I'm impressed (although this was very simple for you experienced programmers) with myself for being able to do this mostly on my own- but I appreciate your help a lot, and I can't believe it wouldn't work just because of something that simple- but I guess that's programming.
     
  7. cazlar macrumors 6502

    Joined:
    Oct 2, 2003
    Location:
    Sydney, Australia
    #7
    Heh, that's programming in a nutshell. One missing bracket, or one in the wrong spot can mean the world of difference, as the computer will do EXACTLY what you tell it to do, which is not necessarily what you mean to do.

    Case in point: the whole Zune shutdown of the last few days was caused by a similarly simple bug:
    Code:
    while (days > 365)
        {
            if (IsLeapYear(year))
            {
                if (days > 366)
                {
                    days -= 366;
                    year += 1;
                }
            }
            else
            {
                days -= 365;
                year += 1;
            }
        }
    So in this case, if it a leap year, and days=366 as it was on Dec 31, you are stuck forever in that loop (at least until the next day)- so no more music for you!

    So the moral is, even small things in code can have big ramifications. As you've noted, heeding warnings early on can spare you a lot of pain later.
     
  8. Arvin Guest

    #8
    Hi,

    I'm running through this challenge now...

    I registered just now to take note on a few things. I don't know if its better or worst, but what I've done seems to be working for me pretty well.

    Code:
    @interface AppController : NSObject {
    	
    	IBOutlet NSTableView *tableView;
    	IBOutlet NSTextField *textField;
    	IBOutlet NSButton *addButton;
    	
    	NSMutableArray *todoList;	
    }
    
    - (IBAction)createNewItem:(id)sender;
    
    1. In the init I use the following:
    Code:
    todoList = [[NSMutableArray array] retain];
    2. My action method createNewItem is as simple as this:
    Code:
    - (IBAction)createNewItem:(id)sender {
    	NSString *item = [textField stringValue];
    	
    	if ([item length] == 0) {
    		return;
    	}
    
    	[todoList addObject:item];
    	NSLog(@"Adding TODO item to list: %@", item);
    	[tableView reloadData];[textField setStringValue:@""];
    	
    	return;
    }
    
    3. For the last piece of the challenge is to make the table view editable. In the reading use the table view setter delegate:
    Code:
    - (void)tableView:(NSTableView *)tableview
       setObjectValue:(id)object
       forTableColumn:(NSTableColumn *)tableColumn
    	            row:(int)row {
    	
    	if (row == -1) {
    		return;
    	}
    	
    	[todoList replaceObjectAtIndex:row withObject:(NSString *)object];
    	NSLog(@"Changed TODO item in list: %@", [todoList objectAtIndex:row]);
    	[tableView reloadData];
    	
    	return;
    }
    
    One last note, if you are over-thinking something, then you are most likely not tackling the challenge correctly.
    Go back and re-read a little bit of the chapter.

    Hope this helps someone.
     

Share This Page