Возможность переупорядочивания UITableView с максимальным количеством строк на раздел - PullRequest
6 голосов
/ 10 января 2010

Я новичок в разработке для iPhone и программировании в целом, недавно завершив Программирование Стивена Кочана в Objective-C 2.0 и части поваренной книги Эрики Садун для iPhone. Просто чтение ответов на вопросы на этом сайте мне очень помогло, поэтому я благодарен всем его пользователям. Сейчас я работаю над своим первым приложением для iPhone и добиваюсь значительных успехов, за исключением одной вещи, которая поставила меня в тупик.

У меня возникли проблемы с UITableView. По сути, у меня есть таблица, в которой должно быть максимум 6 строк на раздел без минимума (за исключением случаев, когда строка раздела из 1 строки удаляется, и в этом случае раздел согласуется с ней). Таблица также может быть переупорядочена пользователем. Когда пользователь перетаскивает строку в раздел, в котором уже есть максимум 6 выделенных строк, я хочу, чтобы нижняя строка этого раздела сортировалась по «бюджету» вниз, чтобы стать верхней строкой следующего раздела.

Что касается реализации этого, моей первой мыслью было вызвать в tableView:moveRowAtIndexPath:toIndexPath: метод, который будет проходить через разделы в цикле, убедившись, что ни один из них не имеет 7+ строк, корректируя массив по мере необходимости, а затем используя [myTable beginUpdates] блок для удаления и вставки необходимых строк. Вот как все это выглядит (примечание: моя структура массива такова: Массив (Таблица)> Массивы (Разделы)> Словари (Элементы)):

-(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!");
}

Проблема в том, что когда я запускаю это, оно неизменно выдает исключение; это дает мне либо 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.), либо Attempted to create two animations for cell, в зависимости от того, какую настройку я пытаюсь выполнить. Что касается этих настроек, я также попытался использовать reloadSections:withRowAnimation: и, конечно, простой [editTable reloadData]. Я попытался поместить вызов этого метода в соответствующие методы делегата / источника данных TableView, включая targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:, и несколько загадочный метод willMoveToRowAtIndexPath:fromIndexPath:, который упоминается в руководстве Apple по TableViews, но отсутствует в других местах.

Я искал в Google, на этом сайте и в документах Apple какие-либо предположения, но безрезультатно.

Итак, проще говоря, как лучше всего это сделать? Есть ли что-то очевидное, что я упускаю из виду? Или моя концепция реализации просто в корне ошибочна с самого начала?

Заранее спасибо за помощь! Любое понимание будет высоко ценится.

- Дрю Р. Худ

ОБНОВЛЕНИЕ: Следуя совету Джордана из его комментария к его ответу, я убедился, что фактические изменения в источнике данных проходят правильно. Теперь я последовательно получаю сообщение об ошибке Attempt to create two animations for cell при выполнении блока редактирования таблицы (это время проверено здравым смыслом и точками останова). Так что я делаю что-то не так в своем блоке редактирования. Текущий код для этого точно так, как показано выше. Идеи?

Ответы [ 2 ]

2 голосов
/ 10 января 2010

[editTable beginUpdates]; [возможно изм deleteRowsAtIndexPaths: budgetFrom withRowAnimation: UITableViewRowAnimationBottom];
[editTable insertRowsAtIndexPaths: BudgetTo withRowAnimation: UITableViewRowAnimationTop];
[editTable endUpdates];

Полагаю, вам необходимо обновить источник данных, прежде чем вы действительно удалите строку в любом разделе, из которого вы ее удаляете. Поэтому в этом коде перед выполнением удаления удалите строку из Table_Array_Macro (которая, как мне кажется, содержит ваши данные).

0 голосов
/ 12 января 2010

Итак, я наконец-то решил проблему и подумал, что опубликую то, что я здесь сделал, ради потомков. Это действительно очень просто: я просто переместил фактическую вставку и удаление строк в таблице в отдельный метод, который я затем вызвал после задержки. С самого начала было ясно, и Джордан помог мне подтвердить, что проблема возникает при выполнении обновлений самой таблицы, а не в процессе фактического изменения данных. Итак, вот код, который добился цели:

-(void) performBudges
{
if ( PM_DEBUG_MODE )
    NSLog(@"Performing budge animations...");

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

[editTable performSelector: @selector(reloadData) withObject: nil afterDelay: 0.5];

[reloadedSections release];

[budgeFrom release];
[budgeTo release];

if ( PM_DEBUG_MODE )
    NSLog(@"Budges completed!");
} 

И все, что мне нужно для этого сделать, это добавить этот фрагмент в мой moveRowAtIndexPath: метод:

if ( [[PET_ICON_DATA objectAtIndex: to.section] count] > 6 ) {

    [self budgeRowsAtSection: to.section];
    [self performSelector: @selector(performBudges) withObject: nil afterDelay: 1.0];

}

Так что я надеюсь, что это поможет любому, у кого может быть эта проблема в будущем. Спасибо Джордану и всем, кто просмотрел этот вопрос. :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...