UITableView :с использованием moveRowAtIndexPath :toIndexPath :и reloadRowsAtIndexPaths :withRowAnimation :вместе выглядят неработающими

Я хочу использовать изящные вызовы перемещения строки -iOS 5 для анимации представления таблицы в соответствии с некоторыми изменениями состояния модели вместо старого стиля -удаления -и вставки -.

Изменения могут включать как переупорядочение, так и обновление места -, и я хочу анимировать и то, и другое, поэтому для некоторых строк потребуется reloadRowsAtIndexPaths.

Но! UITableViewкажется просто неправильным в обработке перезагрузки строк при наличии перемещений, если обновленная ячейка смещает позицию из-за перемещений. Используя старые вызовы удаления + вставки,таким образом, который должен быть эквивалентен, работает нормально.

Вот код; Прошу прощения за многословие, но он компилируется и запускается. Мясо находится в методе doMoves:. Экспозиция ниже.

#define THISWORKS

@implementation ScrambledList // extends UITableViewController
{
  NSMutableArray *model;
}

- (void)viewDidLoad
{
  [super viewDidLoad];
  model = [NSMutableArray arrayWithObjects:
           @"zero",
           @"one",
           @"two",
           @"three",
           @"four",
           nil];
  [self.navigationItem setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:
#ifdef THISWORKS
                                              @"\U0001F603"
#else
                                              @"\U0001F4A9"
#endif
                                                                              style:UIBarButtonItemStylePlain
                                                                             target:self
                                                                             action:@selector(doMoves:)]];
}

-(IBAction)doMoves:(id)sender
{
  int fromrow = 4, torow = 0, changedrow = 2; // 2 = its "before" position, just like the docs say.

  // some model changes happen...
  [model replaceObjectAtIndex:changedrow
                   withObject:[[model objectAtIndex:changedrow] stringByAppendingString:@"\u2032"]];  
  id tmp = [model objectAtIndex:fromrow];
  [model removeObjectAtIndex:fromrow];
  [model insertObject:tmp atIndex:torow];

  // then we tell the table view what they were
  [self.tableView beginUpdates];
  [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:changedrow inSection:0]]
                        withRowAnimation:UITableViewRowAnimationRight]; // again, index for the "before" state; the tableview should figure out it really wants row 3 when the time comes
#ifdef THISWORKS
  [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:fromrow inSection:0]]
                        withRowAnimation:UITableViewRowAnimationAutomatic];
  [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:torow inSection:0]]
                        withRowAnimation:UITableViewRowAnimationAutomatic];
#else // but this doesn't
  [self.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:fromrow inSection:0]
                         toIndexPath:[NSIndexPath indexPathForRow:torow inSection:0]];
#endif
  [self.tableView endUpdates];
}

#pragma mark - Table view data source boilerplate, not very interesting

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
  return model.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@""];
  if (cell == nil)
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@""];
  [cell.textLabel setText:[[model objectAtIndex:indexPath.row] description]];
  [cell.detailTextLabel setText:[NSString stringWithFormat:@"this cell was provided for row %d", indexPath.row]];
  return cell;
}

Что делает код :устанавливает крошечную модель (небольшой изменяемый массив ); когда кнопка нажата, она вносит небольшое изменение в средний элемент списка и перемещает последний элемент на первый. Затем он обновляет представление таблицы, чтобы отразить эти изменения. :перезагружает среднюю строку, удаляет последнюю строку и вставляет новую нулевую строку.

Это работает. На самом деле, добавление ведения журнала к cellForRowAtIndexPathпоказывает, что, хотя я прошу перезагрузить строку 2, табличное представление правильно запрашивает строку 3 из-за вставки, как только пришло время выполнить обновление. Ура!

Теперь закомментируйте верхний #ifdef, чтобы вместо этого использовать вызов moveRowAtIndexPath.

Теперь tableview удаляет строку 2, запрашивает новую строку 2(неправильный! ), и вставляет в последнюю строку -2 позицию (тоже неправильно! ). Конечным результатом является то, что строка 1 переместилась вниз на два слота вместо одного, и прокрутка ее за пределы экрана для принудительной перезагрузки показывает, как она вышла из синхронизации с моделью. Я мог бы понять, если бы moveRowAtIndexPathизменил частную модель табличного представления в другом порядке, требуя использования «новых» вместо «старых» путей индекса при перезагрузке или выборке модели, но это не то, что происходит. Обратите внимание, что на второй картинке «после» третья и четвертая строки расположены в противоположном порядке, чего не должно происходить независимо от того, какую ячейку я перезагружаю.

before state

after one button push, delete-insert style

after one button push, move-style

Мой словарный запас обогатился проклятиями Apple. Должен ли я вместо этого проклинать себя? Являются ли перемещения строк просто несовместимыми с перезагрузкой строк в том же блоке обновлений (, а также, как я подозреваю, вставками и удалениями )? Может ли кто-нибудь просветить меня, прежде чем я отправлю отчет об ошибке?

5
задан rgeorge 7 July 2012 в 17:00
поделиться