initially the mechanics of these were a bit of a mystery. API documentation leaves a lot to be desired…
The bird’s eye view of the process of working with a dynamic table goes something like this:
creating the table:
when ‘reloadData’ is called:
- the table delegate/datasource is asked “How many sections do you have?” and the data source must answer with a positive number.
- the table delegate/datasource is then asked by each section, “I’m in section x, how many cells should I have?”
- the table delegate/datasource is then asked by each individual cell, in order, “I’m cell y from section x, what data should I have?”
and lo, all the cells are filled. at this point there is a one to one association between the data source and the visible table. from this point on, if this ever disagrees, the app will probably crash. these three questions come in the form of these three objective-c calls
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
number three there comes with some boilerplate code when it is generated by Xcode, which is designed to manage only maintaining a relatively small subset of all possible cells. The intent is that a cell is memory intensive, and if you have a long list, most of which is off screen, you shouldn’t waste memory on stuff you can’t see. so the lines:
static NSString *CellIdentifier = @"simpleCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
are there to provide you with just the cells you need to fill at any given moment.
(an index path is a structure that is made up of two members, an array of integers that is the path, and a separate integer that is the length of the first array. technically, indexPaths can be of any length, but in iOS, they are always of length 2. the first number is the section, and the second is the row in that section.)
editing the table:
this was the tricky bit. just because the table loads from the data source, and provides all the funky animated editing modes doesn’t mean that you don’t have to keep the data source synchronized manually.
the basic things that you can do when editing a table structure are:
- delete an item
- add an item
- move an item from one place to another
though it turns out this last thing is just a combination of the previous two.
again, when you do these things, several calls are made to the datasource from the table view to establish a) am I allowed to do this, and if so, b) what are you going to do about it? and it asks slightly different questions for 1 and 2 than it does for 3.
the first thing you have to do to edit the table is enter editing mode. this can be done programmatically, by calling
[self.tableView setEditing:NO animated:YES];
though happily a table view has an inherent ‘edit’ button you can activate by simply calling
self.navigationItem.rightBarButtonItem = self.editButtonItem; during viewDidLoad somewhere.
once you do this, unless you’ve changed something, the table will now show an accessory in each cell to the right or left of the content. to the left, assuming it’s a allowed will either be a green circle with a plus sign or a red circle with a minus sign to add or delete that row. and on the right side, if it’s allowed will be an icon of three horizontal bars that is presumably supposed to be a gripper (or some other iconic indication that you can reposition the cell.) which symbol goes where is controlled by the tableView asking it’s delegate the same questions, once each per cell. and they are:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
the first, obviously, can I edit this row? which dictates whether you get the edit control on the left at all. The second, which dictates which control it is. the first and third are, in fact, optional in that if the table view gets no answer, it assumes yes. you only have to specifically say no. for the second one, if there is no answer, the assumed edit type is ‘delete’ . To repeat, these are optional. you don’t have to implement them if you don’t want to do anything complicated.
However. assuming you have enabled editing at all, once you have performed an edit, the table view will inform its delegate of this and you should then do something about it. these are the methods that it calls to do so.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
The first says to execute the edit based on the style that we optionally set earlier, and the second is simply to move from one place to another. inside these implementations is where you are supposed to actually do the work to keep the view and the model synchronized.
for example, to delete a row from the array and the table view (note that the methods are plural! the first argument is an array.):
[self.listThings removeObjectAtIndex:[indexPath row]];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
and to move a row:
id thingy = [self.listThings objectAtIndex:[fromIndexPath row]];
[self.listThings removeObjectAtIndex:[fromIndexPath row]];
[self.listThings insertObject:thingy atIndex:[toIndexPath row]];
interesting to note that with deleting (and adding) rows, you have to manually remove them from both the tableview and the model. where when you move a row, the table view is taken care of visually and you only have to alter the model to match. (well, you don’t have to, but it seems like a good idea.)
other than custom cells, this pretty much covers every basic thing you need to know to get table views working.
good luck. let me know if I’ve left anything out.