Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reorder-able UITableView with Max # of Rows per Section

I'm new to iPhone dev and programming in general, having recently completed Stephen Kochan's Programming in Objective-C 2.0, and parts of Erica Sadun's iPhone Cookbook. Just reading answered questions on this site has helped me a lot, so I owe a big thanks to all its users. Now I'm working on my first iPhone app, and am making good progress, save for one thing that's got me stumped.

I'm having some trouble with a UITableView. Basically, I have a table that needs to have a maximum of 6 rows per section, with no minimum (except if the row of a 1-row section is deleted, in which case the section goes along with it). The table can also be reordered by the user. When the user drags a row into a section that already has the maximum 6 allotted rows, I want the bottom row of that section to sort-of 'budge' down to become the top row of the next section.

As for implementing this, my first thought was to call a method in tableView:moveRowAtIndexPath:toIndexPath: that would go through the sections in a loop, making sure that none of them had 7+ rows, adjusting the array as needed, and then using a [myTable beginUpdates] block to delete and insert the required rows. This is what all that looks like (note: my array structure is: Array (Table) > Arrays (Sections) > Dictionaries (Items)):

-(void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) from toIndexPath: (NSIndexPath *) to
{ 
// Get the dictionary object for the icon.
NSMutableDictionary *theIcon = [[[TABLE_ARRAY_MACRO objectAtIndex: from.section] objectAtIndex: from.row] mutableCopy];

// Now remove it from the old position, and insert it in the new one.
[[TABLE_ARRAY_MACRO objectAtIndex: from.section] removeObjectAtIndex: from.row];
[[TABLE_ARRAY_MACRO objectAtIndex: to.section] insertObject: theIcon atIndex: to.row];

if ( [[TABLE_ARRAY_MACRO objectAtIndex: to.section] count] > 6 )
          [self budgeRowsAtSection: to.section];

// Now we're done with the dictionary.
[theIcon release];

if ( PM_DEBUG_MODE )
    NSLog(@"Pet moved!");
}



-(void) budgeRowsAtSection: (NSUInteger) section
{
  if ( PM_DEBUG_MODE )
   NSLog(@"Budging rows...");

  // Set up an array to hold the index paths of the cells to move.
  NSMutableArray *budgeFrom = [[NSMutableArray alloc] init];
  NSMutableArray *budgeTo = [[NSMutableArray alloc] init];

  // Start at the current section, and enumerate down to the nearest page with < 6 items.
  int i = section;

 while ( i < [TABLE_ARRAY_MACRO count] ) {

   if ( PM_DEBUG_MODE )
       NSLog(@"Attempting to budge rows in section %i!", i);

   if ( [[TABLE_ARRAY_MACRO objectAtIndex: i] count] > 6 ) {

   // Grab the last object, and move it to the beginning of the next array.
   NSMutableDictionary *lastIcon = [[[PET_ICON_DATA objectAtIndex: i] lastObject] mutableCopy];

   [[TABLE_ARRAY_MACRO objectAtIndex: i] removeLastObject];
   [[TABLE_ARRAY_MACRO objectAtIndex: (i + 1)] insertObject: lastIcon atIndex: 0];

   // Create an index path, and reflect the changes in the table.
   [budgeFrom addObject: [NSIndexPath indexPathForRow: 6 inSection: i]];
   [budgeTo addObject: [NSIndexPath indexPathForRow: 0 inSection: (i + 1)]];

   // Now we're done with the icon.
   [lastIcon release];

 }

 if ( PM_DEBUG_MODE )
      NSLog(@"Rows budged for section %i!", i);

 ++i;

}

  if ( PM_DEBUG_MODE )
    NSLog(@"From cells: %@\nTo cells: %@", budgeFrom, budgeTo);

  if ( PM_DEBUG_MODE )
    NSLog(@"Data budged! Updating table...");

  [editTable beginUpdates];
  [editTable deleteRowsAtIndexPaths: budgeFrom withRowAnimation: UITableViewRowAnimationBottom];
  [editTable insertRowsAtIndexPaths: budgeTo withRowAnimation: UITableViewRowAnimationTop];
  [editTable endUpdates];

  [budgeFrom release];
  [budgeTo release];

  if ( PM_DEBUG_MODE )
   NSLog(@"Row budge done!");
}

The problem is when I run this, it invariably throws an exception; it gives me either Invalid update: number of rows in section after update must be equal to number of rows before update, + or - the number inserted or deleted (etc.), or Attempted to create two animations for cell, depending upon which tweak I'm trying. As for what those tweaks are, I've also tried using reloadSections:withRowAnimation:, and of course a simple [editTable reloadData]. I've tried placing the call to this method in the relevant TableView delegate/data source methods, including targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:, and the somewhat mysterious willMoveToRowAtIndexPath:fromIndexPath: method that is mentioned in the Apple guide on TableViews, but is non-existent elsewhere.

I've searched all over the Google, this site, and Apple's docs for any inkling of a solution, but to no avail.

So, to put my question simply, what is the best way to go about this? Is there some obvious thing that I'm overlooking? Or is my implementation concept just fundamentally flawed from the get-go?

Thanks in advance for the help! Any insight would be much appreciated.

-- Drew R. Hood

UPDATE: Following Jordan's advice from his comment on his answer, I've verified that the actual edits to the data source are going through correctly. I now consistently get the error Attempt to create two animations for cell when the table edit block executes (that timing verified by common sense and breakpoints). So I'm doing something wrong in my edit block. The current code for that is exactly as shown above. Ideas?

like image 922
FrigginGuy Avatar asked Jan 10 '10 15:01

FrigginGuy


1 Answers

[editTable beginUpdates]; [editTable deleteRowsAtIndexPaths: budgeFrom withRowAnimation: UITableViewRowAnimationBottom];
[editTable insertRowsAtIndexPaths: budgeTo withRowAnimation: UITableViewRowAnimationTop];
[editTable endUpdates];

I believe you need to update the DataSource before you actually delete the Row in whatever section you're deleting it from. So in this code, before you do the delete, delete the row from Table_Array_Macro (which I think is where your data is).

like image 148
Jordan Avatar answered Oct 21 '22 16:10

Jordan