当 tableView 仍在滚动时 popToRootViewController 崩溃

Posted

技术标签:

【中文标题】当 tableView 仍在滚动时 popToRootViewController 崩溃【英文标题】:popToRootViewController crashes when tableView is still scrolling 【发布时间】:2014-09-26 13:25:34 【问题描述】:

当我在 tableView 结束滚动之前很好地滑动我的 tableView 并按下“返回”按钮时,我的应用程序崩溃了。我尝试了以下方法:

- (void) closeViewController

    [self killScroll];
    [self.navigationController popToRootViewControllerAnimated:YES];
    [self dismissViewControllerAnimated:YES completion:nil];


- (void)killScroll

    CGPoint offset = sellersTableView.contentOffset;
    [sellersTableView setContentOffset:offset animated:NO];

那没用,同样的崩溃。我不明白为什么,我得到的错误如下:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'

这意味着当所有内容都已被释放时,tableView 仍在请求一个单元格。没有意义。 然后我尝试了这个:

- (void) closeViewController

    [self.navigationController popToRootViewControllerAnimated:YES];
    [self dismissViewControllerAnimated:YES completion:nil];


- (void)dealloc

    sellersTableView.dataSource = nil;
    sellersTableView.delegate = nil;
    sellersTableView = nil;

给我同样的错误。有什么想法吗?

更新: 我的委托方法

创作

if (textField == addSellerTextField) 
        sellersTableView = [[UITableView alloc] initWithFrame:CGRectMake(addSellerTextField.frame.origin.x + addSellerTextField.frame.size.width + 10, addSellerTextField.frame.origin.y - [self heightForTableView] + 35, 200, [self heightForTableView])];
        sellersTableView.delegate = self;
        sellersTableView.dataSource = self;
        sellersTableView.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.05];
        sellersTableView.separatorColor = [[UIColor grayColor] colorWithAlphaComponent:0.15];
        sellersTableView.rowHeight = 44;
        sellersTableView.layer.opacity = 0;
        [self.companyView addSubview:sellersTableView];
        [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^sellersTableView.layer.opacity = 1; completion:nil];
    

cellForRowAtIndexPath

if (tableView == sellersTableView) 
        if (!cell) 
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        
        cell.backgroundColor = [UIColor clearColor];
        if ([sellersArray count] > 0) 
            cell.textLabel.text = [sellersArray objectAtIndex:indexPath.row];
         else 
            UILabel *noSellersYetLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, sellersTableView.frame.size.width, [self heightForTableView])];
            noSellersYetLabel.text = @"no sellers yet";
            noSellersYetLabel.textAlignment = NSTextAlignmentCenter;
            noSellersYetLabel.textColor = [UIColor grayColor];
            [cell addSubview:noSellersYetLabel];
            sellersTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        
    

删除

- (void) textFieldDidEndEditing:(UITextField *)textField

    if (textField == addSellerTextField) 
        [self updateSellers:textField];
    

- (void)updateSellers:(UITextField *)textField

    [textField resignFirstResponder];
    [self hideSellersTableView];


- (void)hideSellersTableView

    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^sellersTableView.layer.opacity = 0; completion:nil];
    sellersTableView.dataSource = nil;
    sellersTableView.delegate = nil;
    [sellersTableView removeFromSuperview];
    sellersTableView = nil;

解决方案 所以显然将dataSource = nildelegate = nil 放入textFieldDidEndEditing 解决了这个问题。谢谢大家的回答!

【问题讨论】:

你能提供一些关于你的 viewController 层次结构的额外信息吗? 检查这是否可以帮助你***.com/questions/19087867/… 【参考方案1】:

这是 UITableView 的奇怪行为。解决此问题的最简单方法是在调用函数popToRootViewControllerAnimated 之前将UITAbleViewdataSourcedelegate 属性设置为nil。此外,您可以使用更常见的解决方案并将将属性设置为 nil 的代码添加到 -dealloc 方法中。另外你不需要-killScroll 方法。

经过短暂的研究,我已经意识到问题所在。这种不寻常的行为出现在 ios 7 中。其 superview 保留的滚动视图可能会在委托释放后向委托发送消息。这是由于-removeFromSuperview 实现UIScrollView 触发-setContentOffset: 并最终向委托发送消息。

【讨论】:

我尝试在关闭前将 dataSource 和 'delegate` 设置为 nil,但我仍然遇到崩溃。谢谢你的信息顺便说一句:)【参考方案2】:

只需在dealloc 方法的开头添加以下行:

sellersTableView.delegate = nil;
sellersTableView.dataSource = nil;

不需要像你的 killScroll 方法那样使用 hack。

另外,我不明白您为什么要同时调用popToRootViewControllerdismissViewController。 如果您关闭嵌入在导航控制器中的视图控制器,导航控制器本身以及所有包含的视图控制器都将被释放。

在你的情况下,你只会有奇怪的动画。

【讨论】:

试过你的方法,但它仍然因同样的错误而崩溃。关于我的popToRootViewControllerdismissViewController,你说得对,我太傻了。 在这种情况下,请提供您的委托和数据源方法的代码。【参考方案3】:

setContentOffset 方法对你没有帮助,尝试设置

sellersTableView.dataSource = nil;

在您的 viewWillDisappear 方法中的某个位置。 这当然不是一个好习惯。

【讨论】:

试过这个,但没有运气。仍然因相同的错误而崩溃。不过谢谢!【参考方案4】:

像下面这样改变你的 closeViewController 看看是否有效

(void) closeViewController

sellersTableView.dataSource = nil;
sellersTableView.delegate = nil;
[self.navigationController popToRootViewControllerAnimated:YES];
[self dismissViewControllerAnimated:YES completion:nil];

【讨论】:

感谢您的回答! Buuuuuut,试过了,结果一样。 我在想的是你没有提供 SellersTableView 与你的 xib/storyboard 的连接。【参考方案5】:

我不认为将 tableView(或它的委托)设置为 nil 是问题所在。您应该能够单独执行dismissViewControllerAnimated 或popToRootViewController 而无需以这种方式修改tableView。

所以问题很可能是由于同时调用这两个方法(并且使用动画 = YES),并且这样做要求您的 viewController 设置做一些不自然的事情。

看起来在点击“关闭”按钮时,您既会弹出rootViewControllerUINavigationController,也会关闭模态viewController

这样做时,您将忽略一个模态viewController,它可能由navigationControllertopViewController 呈现(因此顶部vc 持有对模态vc 的引用)。并且您正试图通过 popToRootViewController 方法调用杀死*** vc。而且您正在使用animated = YES 完成这两件事,这意味着它们需要一些时间才能完成,并且您无法确定每件事何时完成(即您无法确定何时会调用 dealloc)。

根据您的需要,您可以做几件事中的一件。

考虑将委托属性添加到您的模态 vc。关闭模态 vc,并在模态 vc 的 completionBlock 中告诉它的委托它已经完成解散。此时调用 popToRootViewController (因为此时您可以确定模式已消失且滚动未中断)。

如果是以模态方式呈现的是您的 navController,则以相反的顺序执行此操作。通知代理弹出操作已完成,然后执行模态解除。

【讨论】:

我删除了[self dismissViewControllerAnimated:YES completion:nil];,这是我的错误。但除此之外,错误仍然出现并使我的应用程序崩溃。谢谢你的答案!

以上是关于当 tableView 仍在滚动时 popToRootViewController 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

当 tableview 使用自动布局滚动时避免粘性标题视图

当键盘出现iOS时向上滚动tableview行

当键盘出现时,如何阻止 tableView 滚动? [复制]

为啥当我向下滚动iOS(tableView内的collectionview)时取消选择单元格?

将 TableView 标题粘贴到顶部,导致当用户向下滚动时标题不“交互”

如何知道用户仍在 UIScrollView 中触摸屏幕?