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

Posted

技术标签:

【中文标题】UITableView: 使用 moveRowAtIndexPath:toIndexPath: 和 reloadRowsAtIndexPaths:withRowAnimation: 一起出现损坏【英文标题】:UITableView: using moveRowAtIndexPath:toIndexPath: and reloadRowsAtIndexPaths:withRowAnimation: together appears broken 【发布时间】:2012-07-07 06:41:05 【问题描述】:

我想使用 ios 5 漂亮的行移动调用来为 tableview 设置动画以匹配某些模型状态更改,而不是旧式的删除和插入。

更改可能包括重新排序和就地更新,我想为两者设置动画,所以有些行需要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;

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

这行得通。事实上,将日志记录添加到cellForRowAtIndexPath 表明,虽然我要求重新加载第 2 行,但表视图正确地要求第 3 行,因为在实际进行更新时插入。呵呵!

现在注释掉顶部的 #ifdef 以改用 moveRowAtIndexPath 调用。

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

我诅咒苹果的词汇变得丰富多彩。我应该诅咒自己吗?行移动是否与同一更新块中的行重新加载完全不兼容(以及,我怀疑,插入和删除)?在我提交错误报告之前,谁能启发我?

【问题讨论】:

您找到解决方案了吗?我遇到了完全相同的问题。 不!我提交了一个错误报告,该报告以“没有足够的信息”关闭,并请求在 ios6 下测试它(我还没有解决,但如果你遇到问题,它可能没有修复,并且 UICollectionView 可能有类似的问题。)我希望工作的两种解决方法是:执行两个单独的开始/结束更新块,一个用于重新加载,一个用于移动/插入/删除;或完全跳过 reloadRowsAtIndexPaths 并手动定位/重新填充适当的单元格。 【参考方案1】:

我刚刚花了一些时间来研究您的代码,我同意;看起来它只是行不通。

这整个领域的文档有点不足,但他们实际上并没有说您可以将moveRowAtIndexPath:toIndexPath: 与重新加载方法混合使用。 确实说它可以与行插入和行删除方法混合使用。如果我修改您的代码以执行这些代码,这些似乎可行。因此,您可能要求增强,而不是提交错误。不管怎样,我肯定会把它发送到雷达上。

【讨论】:

感谢您的确认。雷达归档。如果 Apple 解决了它,我会追加解决方案。

以上是关于UITableView: 使用 moveRowAtIndexPath:toIndexPath: 和 reloadRowsAtIndexPaths:withRowAnimation: 一起出现损坏的主要内容,如果未能解决你的问题,请参考以下文章

Core Data 更新结果值

使用 UITableView 在 UITableView 上自动滚动 UIImageView

UIView 与 UITableview 和 UIImageview,使用 reloadData 访问 UITableview

UITableView的使用

iOS开发之UITableView的使用

使用UITableView自动在UITableView上滚动UIImageView