UITableView:使用moveRowAtIndexPath:toIndexPath:和reloadRowsAtIndexPaths:withRowAnimation:一起出现损坏

rge*_*rge 5 uitableview uikit ios ios5

我想使用iOS 5的漂亮的行移动调用来动画化表视图以匹配某些模型状态更改,而不是使用旧式的“删除并插入”。

更改可能包括重新排序和就地更新,并且我想同时对它们进行动画处理,因此需要一些行reloadRowsAtIndexPaths

但!UITableView如果更新的单元格由于移动而发生位置偏移,则在存在移动的情况下处理行重新加载时似乎完全是错误的。以等效的方式使用较旧的delete + insert调用,效果很好。

这是一些代码;我为冗长而道歉,但确实可以编译并运行。肉在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;
}
Run Code Online (Sandbox Code Playgroud)

代码的作用:建立一个微型模型(小的可变数组);当按下按钮时,它将对列表的中间元素进行很小的更改,并将最后一个元素移动到第一个元素。然后,它更新表视图以反映这些更改:重新加载中间行,删除最后一行并插入新的零行。

这可行。实际上,向其中添加日志记录cellForRowAtIndexPath表明,尽管我要求重新加载第2行,但由于需要在实际执行更新时进行插入,因此tableview正确地要求第3行。头晕!

现在注释掉最上面的#ifdef,以使用moveRowAtIndexPath调用。

现在,tableview 删除了第2行,询问新的第2行(错!),并将其插入到最后的第2行位置(也错了!)。最终结果是,第1行向下移动了两个插槽,而不是向下移动了两个插槽,并在屏幕外滚动以强制重新加载,这表明该行与模型不同步。我可以理解是否moveRowAtIndexPath以不同的顺序更改了tableview的私有模型,要求在重新加载或获取模型时使用“新”索引而不是“旧”索引路径,但这不是正在发生的事情。请注意,在第二张“之后”图片中,第三行和第四行的顺序相反,无论我要重新加载哪个单元格,都不会发生这种情况。

状态前

一键按下后,删除插入样式

一键按下后,移动式

我的词汇量越来越多,诅咒苹果。我应该诅咒自己吗?行移动是否仅仅是与同一更新块中的行重新加载不兼容的(我怀疑还有插入和删除)?在我提交错误报告之前,谁能启发我?

Jes*_*sak 3

我刚刚花了一些时间玩你的代码,我同意;看起来它根本不起作用。

整个区域的记录有点少,但他们实际上并没有说您可以moveRowAtIndexPath:toIndexPath:与重新加载方法混合使用。它确实在 中说它可以与行插入和行删除方法混合使用。如果我修改你的代码来练习这些,那么这些似乎是有效的。因此,您可能会要求增强功能,而不是提交错误。不管怎样,我肯定会把它发送到雷达。