2012-03-31 52 views
2

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

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

我发布了一个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; 
} 

它允许非常简单的ro WS /部分:

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

你为什么要-loadView设置您的阵列?如果以编程方式创建视图,则应该只覆盖-loadView。没有?并且调用[super loadView]尝试加载与所讨论的视图控制器相同名称的笔尖。为什么不把你的数组设置移动到-viewDidLoad? – 2012-04-03 01:58:43

+0

感谢 - 任何链接/更多信息为什么这是/这个规则来自哪里?当然没有帮助我的问题,但谢谢你的提示。 – 2012-04-03 02:18:35

+0

,因为如果您在nib中设置视图,loadView会被操作系统调用。该方法的文档明确指出不直接调用此方法,并且不要覆盖如果从nib加载视图。 – jmstone617 2012-04-04 16:06:06

回答

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'和任何调用堆栈它恰好在删除调用之前完成其逻辑

+0

谢谢。作为解决方案与使用performSelector相比,这是如何进行比较的:withObject:afterDelay:?是否有任何优点/缺点? PS我觉得有一个);缺失 - 由于最少有6个字符,我的编辑过于小巧 – 2012-04-06 14:56:25

+0

经过一段时间的阅读,看起来GCD确实是一种新颖而有光泽的方式。再次感谢! – 2012-04-06 15:01:24

+1

performSelector变体的缺点是它限制了可能使用的方法签名。这里没有问题,但是如果你需要调用两个方法或者一个带有3个参数的方法,你将无法用performSelector来完成。否则,在这种情况下,延迟0.0基本相同(尽快运行,但在下一次运行循环期间) – bobDevil 2012-04-06 15:31:17

0

在关闭的机会他们可以关注,你有没有检查过你叫resignFirstResponder[view endEditing:YES]?当我们使用文本字段和(IIRC它也取决于iOS 4版本)时,我们看到了这一点,将焦点留在其中一个字段中。

+0

下面的回复我确定我的测试项目没有包括这样的东西(刚刚添加)。 (还是)感谢你的建议。 – 2012-03-31 21:42:42

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

    } 
+0

我不想失去动画,也不能解释我所看到的行为。在我的应用程序的其他地方,我可以删除带有动画的部分,只有当我尝试在moveRow方法中尝试这样做时,才会发现问题。 – 2012-04-02 15:31:38

0

掉在你的代码fromArraytoArray的顺序。如果该项目在将其从阵列中移除之前保留计数为1,那么在将其添加到toArray之前,该项目的保留计数为0。

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

+0

感谢您的提示,但这似乎没有任何区别。如果它是重要的,我正在使用ARC。 – 2012-04-02 15:59:32

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

你的问题是在动画中。一个正在完成,而另一个尚未完成(移动&删除动画)导致一个单元被另一个单元划分。您可以通过再次移动单元格来验证这一点。然后显示正确的顺序。据苹果在UITableView中的docs

注:数据源不应该叫setEditing:动画:从实施的tableView的范围内: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]; 
} 

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

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


    }