How to add rows to tableview

Discussion in 'iOS Programming' started by Glama, Aug 2, 2009.

  1. Glama macrumors member

    Joined:
    Feb 5, 2009
    #1
    Hello,
    I am trying to follow Apple's guide to adding a row to a table view (http://developer.apple.com/iPhone/library/documentation/UserExperience/Conceptual/TableView_iPhone/ManageInsertDeleteRow/ManageInsertDeleteRow.html ) but I dont quite get it.

    I have gotten the edit button to work for deleting a row. One would think that the add button would work the same way, but the guide does it totally differently (and I've had no luck with it.)

    Adding an edit button looks like this:
    Code:
    self.navigationItem.leftBarButtonItem = self.editButtonItem;
    
    So I thought would try
    Code:
    self.navigationItem.rightBarButtonItem = self.addButtonItem;
    
    or
    Code:
    self.navigationItem.rightBarButtonItem = self.insertButtonItem;
    
    but that doesnt compile.

    So before I ask my questions about the "other way" of adding in insert button, am I right in thinking that it must be done with a much different technique?

    Phil
     
  2. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #2
    The concept of Editing is built in to UIViewController. That's why it has an editing property and an editingButtonItem. The concept of adding something isn't a built in concept.

    If you want to have an add button just make a bar button item with a plus sign on it and add it to the navbar.

    Code:
    	NSString*		plusTitle = @"+";
    	UIBarButtonItem*	plusButton = [[UIBarButtonItem alloc] initWithTitle: plusTitle style:UIBarButtonItemStyleBordered target:self action:@selector(addRow:)];
    	self.navigationItem.rightBarButtonItem = plusButton;
    	[plusButton release];
    
     
  3. Glama thread starter macrumors member

    Joined:
    Feb 5, 2009
    #3
    Thanks for the reply, PhoneyDeveloper.

    This is the "much different technique" i was speaking of. So in your code example, I need to create a method called addRow, correct? This is where I get lost. What type does addRow need to return? Can I reload the tableview from addRow?
     
  4. Glama thread starter macrumors member

    Joined:
    Feb 5, 2009
    #4
    Maybe I should also ask, if adding a row from the nav bar isnt a built in concept, what is the "built in technique" for adding rows to a tableview? Surely there is one?
     
  5. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #5
    Lotta questions.

    Is there an app in the Apple apps that does what you want to do? Contacts maybe?

    You add rows to a table view two ways: add rows to your data model and reloadData, use a beginUpdates/endUpdates block and add rows inside there. Which of those you choose has to do mostly with the UI that you want.

    In either case you start the process in the action method that I mentioned, addRow, which goes into your view controller. It can have any name of course.

    Code:
    -(IBAction)addRow
    {
    // add your row here or whatever
    }
    
    You can reloadData in addRow if you want.
     
  6. Glama thread starter macrumors member

    Joined:
    Feb 5, 2009
    #6
    Ok, so I have pretty much what you posted to create the button:

    Code:
    	UIBarButtonItem*	addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addRow:)];
    	self.navigationItem.rightBarButtonItem = addButton;
    	[addButton release];
    
    And now this for addRow:

    Code:
    - (IBAction)addRow{
    	
      [dataController addData:@"New Row Added"];      
      [self.tableView reloadData];
     
    }
    
    But I get "terminating_due_to_uncaught_exception" when I click the add button. Is the problem obvious? Is there a way to see more specific info about the error?
     
  7. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #7
    The reason for the uncaught exception is that this

    Code:
    @selector(addRow:)
    doesn't match this

    Code:
    - (IBAction)addRow
    The colon at the end of the selector string indicates that there will be a parameter. I guess that was my mistake in the code I showed. Change it to

    Code:
    @selector(addRow)
    To answer your question though: If you look at the debugger console you'll see more details about any run time exceptions like this.

    Also, you need to add a breakpoint. Open up the breakpoints window and double click the blue cube to add a new breakpoint. Type in this function name

    objc_exception_throw

    Then the debugger will break if a run time exception is throws and you'll get a stack trace and a better idea where and why it's happening.
     
  8. Glama thread starter macrumors member

    Joined:
    Feb 5, 2009
    #8
    Ah, great info about the debugger, and your change to the code that creates the button fixed one problem. I think now the problem is that I am not adding info to the dictionary, and I need to be..


    Code:
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];
        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"MyIdentifier"] autorelease];
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        }
        
        // Get the object to display and set the value in the cell
        NSDictionary *itemAtIndex = (NSDictionary *)[dataController objectInListAtIndex:indexPath.row];
        cell.textLabel.text = [itemAtIndex objectForKey:@"title"];
        return cell;
    }
    
     
  9. Glama thread starter macrumors member

    Joined:
    Feb 5, 2009
    #9
    I think the key thing I have to figure out is how to add a new row to the data controller. Im working with a sample app (Apple's 'SimpleDrillDown' sample) that uses a dictionary to hold the data instead of just an array.

    So Im trying to experiment with adding rows manually but still can figure out whats going on.

    This code works:
    Code:
        characters = [[NSArray alloc] initWithObjects:@"Antony", @"Artemidorus", @"Brutus", @"Caesar", @"Calpurnia", @"Casca", @"Cassius", @"Cicero", @"Cinna", @"Cinna the Poet", @"Citizens", @"Claudius", nil];
    
    dictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"Julius Caesar", @"title", characters, @"mainCharacters", nil];
    
    Somehow the "Julius Caesar" ends up on the root table view, and the characters show up on the drilldown table view.

    I want to be able to programatically add a row to the root tableview, but have had no luck. So Im trying to add a row manually to the initial data. Even that doesnt work. My latest attempt looked like this:
    Code:
        characters = [[NSArray alloc] initWithObjects:@"Antony", @"Artemidorus", @"Brutus", @"Caesar", @"Calpurnia", @"Casca", @"Cassius", @"Cicero", @"Cinna", @"Cinna the Poet", @"Citizens", @"Claudius", nil];
        plays = [[NSArray alloc] initWithObjects:@"Julius Caesar", @"Sam the Cat", nil];
    
        dictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:plays, @"title", characters, @"mainCharacters", nil];
    
    but this crashes. I dont know why I can add an array to the dictionary with a long list of "characters", but when I try the same technique with Plays, it doesnt work. I looked at the console and it said this:

    Problem parsing arguments: break-insert -f --
    Unable to set breakpoint
    objc_exception_throw. Make sure to build the file
    objc_exception_throw with debugging symbols.
    objc_exception_throw"

    Any ideas?

    Once I set a breakpoint for objc_exception_throw, how do I unset it?
     
  10. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #10
    You don't say where it crashes or how it crashes.

    But in your cellForRowAtIndexPath the code seems to expect an NSString is stored in the dictionary with key @"title" but you're putting an array of strings there called characters with that key.

    I haven't looked at that example code in a while but tables are usually modeled as an array of dictionaries. The array represents the list of rows in the table and the dictionaries represent each row. So in cellForRowAtIndexPath you get the dictionary for the requested row and then you read the data out of the dictionary for the fields in the cell.
     
  11. Glama thread starter macrumors member

    Joined:
    Feb 5, 2009
    #11
    You are right about how the program works, even though I've posted very little of it. You really know your stuff. Thank you for your help.

    I looked more closely at the cellForRowAtIndexPath method for both the rootview controller and the detailViewController, and I understand (better) why I wasnt able to treat one the same as the other. The code in detailViewController is using objectAtIndex
    Code:
    cellText = [(NSArray *)[detailItem objectForKey:@"mainCharacters"] objectAtIndex:indexPath.row];
    
    whereas the code in the rootViewController is using objectForKey
    Code:
    cell.textLabel.text = [itemAtIndex objectForKey:@"title"];
    
    Im still getting my head wrapped this stufff. The concept of an array of dictionaries with more dictionaries inside them is still sinking in. Although the contents for the root tableview are not in a "sub" dictionary for this "simple" sample program Im working with, it seems like I would want it in a sub dictionary later on when I want to add more content to each cell than just a single text string. Does that sound correct? I dont know how reordering of rows works yet and other stuff that I will encounter down the road so I dont know what I'd be getting myself into if I do that. Im still working on programatically adding a row to the root tableview. :rolleyes:
     
  12. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #12
    Usually the dictionary is used to represent a row. It can contain various kinds of values, like strings or images of icons, or even sounds or other kinds of objects. The object that holds the row dictionary represents some structure in the table. In a plain table you just have an array of dictionaries. In a grouped table I usually have an array of arrays of dictionaries, where each of the second arrays represents a section. cellForRowAtIndexPath just needs to get the right dictionary and it can set the contents of the cell.

    In the case where I have a master, detail interface, like you describe, then the master list will only show part of the info for the item being displayed in a row. When you tap the row it pushes a detail view controller. It takes the dictionary that represented that row and then displays more of it or all of it.

    If you look through Apple's Settings app you can see how it goes from master to detail view and you can imagine how the data are structured.
     
  13. Glama thread starter macrumors member

    Joined:
    Feb 5, 2009
    #13
    Thanks, PhoneyDeveloper. I studied dictionaries a little more and understood better what I need to do, and now I am able to add a row to my tableview. :)

    I am getting a warning I dont understand though.

    My code to add a row looks like this:

    Code:
    - (IBAction)addRow{
    	
    	
            NSMutableDictionary *dictionary;
            NSMutableDictionary *characters;
        
    	characters = [[NSArray alloc] initWithObjects:@"Henry", @"Cali", nil];
    	dictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"Sam the Cat", @"title", characters, @"mainCharacters", nil];
    	[dataController addData:dictionary];
    	[dictionary release];
    	[characters release];
    			
    	
    	[self.tableView reloadData];
     	
    }
    
    And I am calling this addData method in my datacontroller:
    Code:
    - (void)addData:(NSMutableDictionary*)data;
    {
    	[list addObject:data];
    }
    
    @end
    
    The warning I get is "passing argument 1 of addData from distinct Objective-C type".

    I dont know what that means. Can anyone tell me what Im doing wrong?
     
  14. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #14
    That error indicates that the specified parameter doesn't match the type in the method prototype. So if you try to pass an NSArray when the prototype wants an NSDictionary you get that error. However I don't see the problem in the code you show. What does the prototype in the header file look like for addData?

    Also, characters is an array but you assign it to a dictionary *.
     
  15. Glama thread starter macrumors member

    Joined:
    Feb 5, 2009
    #15
    The prototype looks like this:
    Code:
    - (void)addData:(NSString*)data;
    
    So I see the problem. I need it to be
    Code:
    - (void)addData:(NSMutableArray*)data;
    
    correct?

    And I see what you mean about characters. It looks like an array to me too but is assigned to a dictionary. That part of the code is actually from Apple's "simple drilldown" sample app so I dont know why they did it that way. I guess I can experiment with changing it as I turn this sample app into something else.

    Thanks again for all your help.
     
  16. Glama thread starter macrumors member

    Joined:
    Feb 5, 2009
    #16
    I still got it wrong. The prototype should be:
    Code:
    - (void)addData:(NSMutableArray*)data;
    
    That solved it :)
     

Share This Page