将最后一行移出部分并删除部分时出现奇怪的动画

时间:2012-03-31 05:41:26

标签: iphone objective-c ios uitableview

我有一个多节的tableview。在编辑模式下,我允许行从一个部分移动到另一个部分。从一个部分删除最后一行后,我删除该部分。所以我在moveRowAtIndexPath中使用deleteSection。

当从该部分移动最终项目时,部分标题将按计划消失。但是有一个非常奇怪的动画错误,其中移动的行似乎与上面删除的行“合并”,并且“to”部分底部显示一个空行(可能是因为该部分的numberOfRows是正确,但2行在同一位置)。更奇怪的是,当我点击此行的重新排序控件(不移动项目,只需触摸和释放)时,这两个项目“取消合并”。

我发布了一个video来证明这一点。

我已尝试包装我的数据更改并查看开始/结束更新中的更改,但无济于事。

我上传了一个测试项目here,我也会发布以下代码。几点:

  • 我试图在演示项目中复制我的数据源格式,以防出现问题。关键是我的源是一个由两个其他数组组成的复合数组(虽然我不明白为什么这会是一个问题)。
  • 要查看相关行为,请移动底部的两行,直到顶部。不要把它们放在顶部的最后一行,因为这似乎没问题。
  • 在此演示项目中,从顶部到底部以相反的方式移动行。

代码(所有这些都在演示项目中):

我在loadView中设置了数组:

- (void)loadView{
array1 = [NSMutableArray array];
[array1 addObject:@"test 0"];
[array1 addObject:@"test 1"];
[array1 addObject:@"test 2"];

    array2 = [NSMutableArray array];
    [array2 addObject:@"test a"];
    [array2 addObject:@"test b"];

    [super loadView];
}

我还有一个方法可以返回这些数组的组合 - 这用作数据源:

- (NSMutableArray *)sourceArray{
NSMutableArray *result = [NSMutableArray array];
if (array1.count > 0) {
    [result addObject:array1];
}
if (array2.count >0) {
    [result addObject:array2];
    }
    return result;
}

允许非常简单的行/节数:

   - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return self.sourceArray.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [[self.sourceArray objectAtIndex:section] count];
}

标准单元格/标题格式:

  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    cell.textLabel.text = [[self.sourceArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
    return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return [NSString stringWithFormat:@"Section %i", section];
}

这就是我做魔术的地方

    // Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
    NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section]; 
    NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];

    NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];

    [fromArray removeObject:movedObject];
    [toArray insertObject:movedObject atIndex:toIndexPath.row];

        if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
        }
    }

7 个答案:

答案 0 :(得分:3)

我注意到来自待删除部分的行是在您修饰订单控件之前消失的行。

我怀疑当tableview调用此数据源方法时,其状态仍处于执行移动的过程中,因此调用'deleteSections'将使表尝试删除您正在移动的行。这并不是一个合并,因为它以与节头相同的速度逐渐消失,而它下面的那个正在缩小以填补空间。

点击控件会导致表格视图重新调整,并意识到该行实际上并没有消失。

尝试解决此问题,尝试通过调度调用在下一个runloop中运行删除,如:

if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
    dispatch_async(dispatch_get_main_queue(), ^() {
        [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
    });
}

这将导致删除仍然在主线程上运行,但允许'moveRow'和它发生的任何调用堆栈在删除调用之前完成其逻辑

答案 1 :(得分:2)

您的问题出在动画中。一个正在完成,而另一个尚未完成(移动和删除动画),导致一个单元格被绘制在另一个单元格上。您可以通过再次移动单元格来验证这一点。然后会显示正确的顺序。根据Apple在UITableView上的docs

  

注意:数据源不应该从tableView的实现中调用setEditing:animated::commitEditingStyle:forRowAtIndexPath:。如果由于某种原因必须使用performSelector:withObject:afterDelay:方法,它应该在延迟之后调用它。

因此要解决此问题,请执行以下操作:

if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
   [self performSelector:@selector(someMethod:) withObject:fromIndexPath afterDelay:1.0];
}

- (void) someMethod:(NSIndexPath *) fromIndexPath {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section]  withRowAnimation:UITableViewRowAnimationFade];
}

应该可以正常工作。只需将延迟更改为适合您的延迟。

答案 2 :(得分:0)

如果您的行或其中的内容可以成为焦点,您是否检查过您已调用resignFirstResponder[view endEditing:YES]?我们在使用文本字段时看到了这一点,并且(IIRC也依赖于iOS 4版本)将焦点留在了其中一个字段中。

答案 3 :(得分:0)

删除该部分后,您必须重新加载tableview。试试这段代码。

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
    NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section]; 
    NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];

    NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];

    [fromArray removeObject:movedObject];
    [toArray insertObject:movedObject atIndex:toIndexPath.row];

        if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView reloadData];
        }

    }

答案 4 :(得分:0)

在代码中交换fromArraytoArray的顺序。如果项目在从数组中删除之前保留计数为1,则在将其添加到toArray之前,其保留计数为0。

如果您更换订单,该项目将从保留计数1到2,然后在删除完成后返回到1。

答案 5 :(得分:0)

我认为UITableViewRowAnimationFade动画干扰了UITableViewCell移动动画。您可以尝试的一件事是延迟部分删除有点迟,以便单元格移动行动画完成。

尝试使用以下代码替换您的代码。

-(void)deleteSection:(NSIndexSet*)indexSet
{
    [self.tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationFade];
}

// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
    NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section]; 
    NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];

    NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];

    [fromArray removeObject:movedObject];
    [toArray insertObject:movedObject atIndex:toIndexPath.row];

    if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
        [self performSelector:@selector(deleteSection:) withObject:[NSIndexSet indexSetWithIndex:fromIndexPath.section] afterDelay:1.0];
//      [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
    }
}

答案 6 :(得分:0)

在最后一行丢失动画的解决方案:

if([listOfItemsOnTransaction count]==indexPath.row){ 
  [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]     
   withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView reloadData];
   }else
   {
       [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] 
         withRowAnimation:UITableViewRowAnimationFade];


   }