UITableView:使用moveRowAtIndexPath:toIndexPath:和reloadRowsAtIndexPaths:withRowAnimation:一起出现破碎 [英] UITableView: using moveRowAtIndexPath:toIndexPath: and reloadRowsAtIndexPaths:withRowAnimation: together appears broken

查看:126
本文介绍了UITableView:使用moveRowAtIndexPath:toIndexPath:和reloadRowsAtIndexPaths:withRowAnimation:一起出现破碎的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用iOS 5的漂亮的行移动调用来为tableview设置动画以匹配某些模型状态更改,而不是旧式的delete-and-insert。

I want to use iOS 5's nifty row-movement calls to animate a tableview to match some model state changes, instead of the older-style delete-and-insert.

更改可能包括重新排序和就地更新,我想为两者设置动画,因此某些行需要 reloadRowsAtIndexPaths

Changes may include both reordering and in-place updates, and I want to animate both, so some rows will need reloadRowsAtIndexPaths.

但是! UITableView 在移动存在的情况下,如果更新的单元格由于移动而移位,则在处理行重新加载时似乎完全错误。使用较旧的delete + insert调用,应该是等效的,可以正常工作。

But! UITableView appears to be just plain wrong in its handling of row reloads in the presence of moves, if the updated cell shifts position because of the moves. Using the older delete+insert calls, in a way that should be equivalent, works fine.

这是一些代码;我为冗长而道歉,但它确实编译并运行。肉位于 doMoves:方法中。下面的博览会。

Here's some code; I apologize for the verbosity but it does compile and run. The meat is in the doMoves: method. Exposition below.

#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;
}

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

What the code does: sets up a tiny model (small mutable array); when a button is pushed, it makes a small change to the middle element of the list, and moves the last element to be the first. Then it updates the table view to reflect these changes: reloads the middle row, removes the last row and inserts a new row zero.

这有效。事实上,添加日志记录到 cellForRowAtIndexPath 表明虽然我要求重新加载第2行,但是tableview正确地要求第3行,因为插入一旦它实际执行了更新。 Huzzah!

This works. In fact, adding logging to cellForRowAtIndexPath shows that although I ask for row 2 to be reloaded, the tableview correctly asks for row 3 because of the insert once it's time to actually do the update. Huzzah!

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

Now comment out the top #ifdef to use the moveRowAtIndexPath call instead.

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

Now the tableview removes row 2, asks for a fresh row 2 (wrong!), and inserts it in the final row-2 position (also wrong!). Net result is that row 1 moved down two slots instead of one, and scrolling it offscreen to force a reload shows how it's gone out of sync with the model. I could understand if moveRowAtIndexPath changed the tableview's private model in a different order, requiring the use of the "new" instead of "old" index paths in reloads or model fetches, but that's not what's going on. Note that in the second "after" pic, the third and fourth rows are in the opposite order, which should't happen no matter which cell I'm reloading.

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

My vocabulary has grown colorful cursing Apple. Should I be cursing myself instead? Are row moves just plain incompatible with row reloads in the same updates block (as well as, I suspect, inserts and deletes)? Can anyone enlighten me before I go file the bug report?

推荐答案

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

I just spent some time playing with your code, and I agree; looks like it just doesn't work.

整个区域有点记录不足,但他们实际上并没有说你可以混合 moveRowAtIndexPath:toIndexPath:使用重装方法。 表示它可以与行插入和行删除方法混合使用。如果我修改你的代码来代替它们,那些似乎是有效的。所以,你可能会要求增强,而不是提交错误。无论哪种方式,我肯定会将它发送到雷达。

This whole area is a bit under-documented, but they don't actually say that you can mix moveRowAtIndexPath:toIndexPath: with reload methods. It does say in the that it can be mixed with row-insertion and row-deletion methods. Those seems to work if I modify your code to exercise those instead. So, you might be asking for an enhancement, not filing a bug. Either way, I'd definitely send it to radar.

这篇关于UITableView:使用moveRowAtIndexPath:toIndexPath:和reloadRowsAtIndexPaths:withRowAnimation:一起出现破碎的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆