简体   繁体   English

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

[英]Strange animation when moving last row out of section and deleting section

I have a multi-section tableview. 我有一个多部分的tableview。 In edit mode I allow rows to be moved from one section to another. 在编辑模式下,我允许将行从一个部分移到另一部分。 Once the final row is removed from one section I delete that section. 将最后一行从一个部分中删除后,我将删除该部分。 So I am using deleteSection inside moveRowAtIndexPath. 所以我在moveRowAtIndexPath内部使用deleteSection。

When the final item is moved from the section, the section header disappears as planned. 当最后一个项目从节中移出时,节标题将按计划消失。 But there is a very strange animation bug, where the moved row seems to 'merge' with the row it is dropped above, and an empty row is displayed at the bottom of the 'to' section (probably because the numberOfRows for that section is correct, but 2 rows are in the same position). 但是有一个非常奇怪的动画错误,其中移动的行似乎与它上方的行“合并”,并且在“ to”部分的底部显示一个空行(可能是因为该部分的numberOfRows为正确,但2行位于同一位置)。 Even stranger, when I click the reorder control for this row (not moving the item, simply touching and releasing), the two items 'unmerge'. 甚至更陌生,当我单击该行的重新排序控件(不移动项目,只需触摸并释放)时,两个项目“合并”。

I have posted a video demonstrating this. 我发布了一个视频来演示这一点。

I have tried wrapping my data changes and view changes in begin/end updates, but to no avail. 我尝试包装我的数据更改并在开始/结束更新中查看更改,但无济于事。

I have uploaded a test project here , and I will also post the code below. 我已经在这里上传了一个测试项目,我还将在下面发布代码。 A couple of points: 要点:

  • I have tried to replicate my data source's format in the demo project, in case this is where the problem originates. 我试图在演示项目中复制数据源的格式,以防出现问题。 The key thing is that my source is a composite array of two other arrays (though I can't see why this would be an issue). 关键是我的源代码是其他两个数组的组合数组(尽管我看不到为什么会是个问题)。
  • To see the behavior in question, move the two rows in the bottom section, up into the top section. 若要查看问题的行为,请将底部的两行向上移动到顶部。 Don't drop them in the last row on the top section though, since this seems to work ok. 但是不要将它们放在顶部的最后一行中,因为这似乎可以正常工作。
  • Moving rows the other way, from the top section to the bottom section, is buggy in this demo project. 在本演示项目中,将行从顶部移动到底部的另一种方式是错误的。

Code (all of this is in the demo project): 代码(所有这些都在演示项目中):

I set up my arrays in loadView: 我在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];
}

I also have a method that returns a combination of these arrays - this is used as the data source: 我也有一个返回这些数组组合的方法-用作数据源:

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

Which allows for very simple number of rows/sections: 这使得行/节的数量非常简单:

   - (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];
}

Standard Cell/Header formatting: 标准单元格/标题格式:

  - (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];
}

This is where I do the magic 这是我做魔术的地方

    // 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];
        }
    }

I notice that the row that comes from the to-be-deleted section is the one that disappears until you retouch the order control. 我注意到,来自待删除部分的行将消失,直到您修改订单控件。

I suspect that when this datasource method is called by the tableview, its state is still in the middle of performing the move, so calling 'deleteSections' will make the table try and delete the row you're moving. 我怀疑当tableview调用此数据源方法时,它的状态仍处于执行移动的中间,因此调用'deleteSections'将使表尝试删除正在移动的行。 It's not so much of a merge as the fact that it's fading away at the same rate as the section header, and the one below it is just scooting back up to fill the space. 它并没有太大的合并,因为它以与节标题相同的速率逐渐消失,而它下面的那个只是在回弹以填充空间。

Tapping the control causes the table view to rejigger itself and realize that the row isn't actually gone. 轻击控件会使表视图重新调整自身位置,并意识到该行实际上并未消失。

to try and work around this, try running the deletion in the next runloop, via a dispatch call, like: 要尝试解决此问题,请尝试通过调度调用在下一个runloop中运行删除操作,例如:

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

this will cause the deletion to run on the main thread still, but allow the 'moveRow' and whatever call stack it happens to be in finish up its logic before the deletion call 这将导致删除操作仍在主线程上运行,但允许'moveRow'及其碰巧发生的任何调用栈在删除调用之前完成其逻辑

Your problem is in the animation. 您的问题出在动画上。 One is being done while another is not yet finished (moving & deleting animation) causing one cell to be drawn upon the other. 一个正在完成而另一个尚未完成(移动和删除动画),导致一个单元格被绘制在另一个单元格上。 You can verify this by moving the cells around again. 您可以通过再次移动单元格来验证这一点。 The correct order will then be displayed. 然后将显示正确的顺序。 According to Apple's docs on the UITableView: 根据Apple在UITableView上的文档

Note: The data source should not call setEditing:animated: from within its implementation of tableView:commitEditingStyle:forRowAtIndexPath:. 注意:数据源不应在tableView:commitEditingStyle:forRowAtIndexPath:的实现中调用setEditing:animated:。 If for some reason it must, it should invoke it after a delay by using the performSelector:withObject:afterDelay: method. 如果出于某种原因,它必须在延迟后通过使用performSelector:withObject:afterDelay:方法来调用它。

Therefore to fix this, do this to your code: 因此,要解决此问题,请对您的代码执行以下操作:

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

Should work fine. 应该工作正常。 Just change the delay to something shorter that suites you. 只需将延迟更改为更适合您的时间即可。

I think the UITableViewRowAnimationFade animation is interfering with the UITableViewCell move animation. 我认为UITableViewRowAnimationFade动画会干扰UITableViewCell移动动画。 One thing you can try is to delay the section deletion a little bit late in order for the cell move row animation to finish. 您可以尝试做的一件事是延迟部分删除,以使单元格移动行动画完成。

Try replace your code with the following code. 尝试用以下代码替换您的代码。

-(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];
    }
}

a solution that lost animation on last row : 在最后一行丢失动画的解决方案:

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];


   }

On the off chance that your rows or what's inside them can take focus, have you checked that you have called resignFirstResponder or [view endEditing:YES] ? 如果行或行中的内容可以引起焦点,您是否检查过是否调用过resignFirstResponder[view endEditing:YES] We saw this when we used text fields and (IIRC it was iOS 4 version dependent too) left the focus in one of the fields. 我们在使用文本字段时看到了这一点,并且(IIRC也依赖于iOS 4版本)将焦点放在其中一个字段上。

You have to reload the tableview after deleting the section. 删除该部分后,您必须重新加载tableview。 Try this code. 试试这个代码。

- (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];
        }

    }

Swap the order of fromArray and toArray in your code. 在代码中交换fromArraytoArray的顺序。 If the item has a retain count of 1 prior to removing it from the array, it will have a retain count of 0 before adding it to toArray . 如果该项目从数组中取出具有现有的1一个保留计数,它将具有其添加到之前一个保留的0计数toArray

If you swap the order, the item will go from retain count of 1 to 2 then back to 1 when the remove is complete. 如果您交换订单,则该项目将从保留计数1变为2,然后在删除完成后返回1。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM