UITableView:使用moveRowAtIndexPath:toIndexPath:和reloadRowsAtIndexPaths:withRowAnimation:一起出现问题

5
我想使用iOS 5的巧妙行移动调用来实现对tableview的动画处理,以匹配某些模型状态的更改,而不是使用旧的删除和插入方式。
这些更改可能包括重新排序和就地更新,并且我希望同时对它们进行动画处理,因此某些行将需要使用“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行,但在实际执行更新时,tableview正确地请求第3行,因为有了插入操作。现在注释掉顶部的#ifdef,使用moveRowAtIndexPath调用代替。现在,tableview删除第2行,请求一个新的第2行(错误!),并将其插入到最终的倒数第2个位置(也是错误的!)。结果是第1行向下移动了两个槽位而不是一个,并且滚动它使其离屏幕强制重新加载,显示它已经与模型不同步。我可以理解如果moveRowAtIndexPath以不同的顺序更改了tableview的私有模型,则需要在reload或模型获取中使用“new”而不是“old”索引路径,但事实并非如此。请注意,在第二个“after”图片中,第三和第四行的顺序相反,无论我重新加载哪个单元格都不应该发生这种情况。我的词汇量变得色彩斑斓,诅咒苹果。我应该诅咒自己吗?行移动是否与同一更新块中的行重新加载(以及我怀疑的插入和删除)不兼容?在我提交错误报告之前,有人可以启发我吗?

你有没有找到解决办法?我遇到了完全相同的问题。 - A. Z.
不行!我提交了一个错误报告,但被关闭并要求在ios6下测试它(我还没有时间去测试,但如果你也遇到这个问题,那么它可能没有被修复,而UICollectionView可能存在类似的问题)。我预计会有两种解决方法:分别使用两个begin/endUpdates块,一个用于reload,另一个用于move/insert/delete;或者完全跳过reloadRowsAtIndexPaths,手动定位/重新填充相应的单元格。 - rgeorge
1个回答

3

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

这整个区域有点缺乏文档,但他们实际上没有说你可以混合使用moveRowAtIndexPath:toIndexPath:和reload方法。在文档中确实提到它可以与行插入和行删除方法混合使用。如果我修改你的代码以使用这些方法,它们似乎可以工作。所以,你可能是要求增强功能,而不是提交错误报告。无论如何,我肯定会将其发送到radar。


感谢确认。雷达已提交。如果苹果解决了这个问题,我会追加解决方案。 - rgeorge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接