UITableView没有窗口iOS13时的UITableViewAlertForLayoutOutsideViewHierarchy

Posted

技术标签:

【中文标题】UITableView没有窗口iOS13时的UITableViewAlertForLayoutOutsideViewHierarchy【英文标题】:UITableViewAlertForLayoutOutsideViewHierarchy when UITableView doesn't have window iOS13 【发布时间】:2019-09-27 07:49:31 【问题描述】:

我开始在 ios13 上收到警告(下)。我注意到弹出此警告是因为UITableView 的窗口为空(选择了另一个选项卡,在表选择上推送了详细视图控制器...)。 我正在尝试从NSFetchedResultController 代表更新UITableView。在 iO13 上执行此操作以保持表格更新的正确方法是什么?

以下代码在以前的版本中运行良好。

PS:任何类型的beginUpdatesreloadRowsAtIndexPaths:withRowAnimation:insertSections:withRowAnimation:endUpdates都会导致这个警告。

PS:我尝试重新加载表格,但如果我向后导航,我会失去动画以取消选择行(清除行选择)。

2019-09-27 09:40:42.849128+0200 xxx[63595:9762090] [TableView] 警告 仅一次:UITableView 被告知布局其可见单元格和其他 不在视图层次结构中的内容(表视图或其中之一 它的超级视图尚未添加到窗口中)。这可能会导致错误 强制表格视图内的视图加载并执行布局 准确的信息(例如表视图边界、特征集合、布局 边距、安全区域插图等),也会导致不必要的 由于额外的布局传递而导致的性能开销。做一个象征性的 UITableViewAlertForLayoutOutsideViewHierarchy 的断点来捕捉 这在调试器中,看看是什么导致了这种情况发生,所以你可以 如果可能的话完全避免这个动作,或者推迟到餐桌上 视图已添加到窗口中。表视图:; 层 = ;内容偏移:0,-64; 内容大小:375, 3432;调整内容插入:64, 0, 0, 0; 数据来源:>

// ------------  ------------  ------------  ------------  ------------  ------------
#pragma mark - FetchedResultsController delegate

- (void) controllerWillChangeContent:(NSFetchedResultsController *)controller 
//    if (self.tableView.window) 
        [self.tableView beginUpdates];
//    

- (void) controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath 

    if (type == NSFetchedResultsChangeInsert && newIndexPath != nil) 
        [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    

    if (type == NSFetchedResultsChangeUpdate && indexPath != nil) 
        [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
//        id<CellLoadable> cell = [self.tableView cellForRowAtIndexPath:indexPath];
//        [cell loadData:anObject];
    

    if (type == NSFetchedResultsChangeMove && indexPath != nil && newIndexPath != nil) 
        // if cell is visible, update it
        id<CellLoadable> cell = [self.tableView cellForRowAtIndexPath:indexPath];
        [cell loadData:anObject];
        [self.tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
    


- (void) controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type 

    if (type == NSFetchedResultsChangeInsert) 
        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
    
    if (type == NSFetchedResultsChangeDelete) 
        [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
    


- (void) controllerDidChangeContent:(NSFetchedResultsController *)controller 
//    if (self.tableView.window) 
        [self.tableView endUpdates];
//    

【问题讨论】:

你不是唯一一个。我每次都会收到这个警告,与可区分的数据源有关。还有人在这里:forums.developer.apple.com/thread/120790 我觉得他们希望我们实现 iOS13 中引入的 diffable 数据源 我使用它们,也在那里得到警告 【参考方案1】:

PS:任何类型的 beginUpdates 、 reloadRowsAtIndexPaths:withRowAnimation: 、 insertSections:withRowAnimation: 、 endUpdates 都会导致此警告。

我发现将表更新包装在 dispatch_async 中触发断点的位置可以消除问题:

dispatch_async(dispatch_get_main_queue(), ^(void)
    [self.table reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
);

(可能必须在调用堆栈中断时才能找到调用)

【讨论】:

【参考方案2】:

您可以尝试下面的代码,即检查窗口并改为调用 reloadData。仅供参考,这实际上并没有重新加载所有单元格,它只是调用行数、节数等。然后在接下来出现的新单元格将被加载。当视图消失并在下次出现时重新加载表时,您最好禁用获取控制器。

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 
    if(!self.tableView.window)
        return;
    
    [self.tableView beginUpdates];


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type 
    if(!self.tableView.window)
        return;
    
    switch(type) 
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        default:
            return;
    


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath 
    UITableView *tableView = self.tableView;
    if(!tableView.window)
        return;
    
    switch(type) 
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeMove:
            [tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] withEvent:anObject];
            break;
    


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
    if(!self.tableView.window)
        [self.tableView reloadData];
        return;
    
    [self.tableView endUpdates];

注意,您可能还想重新选择以前选择的单元格。有几种不同的方法可以做到这一点。

【讨论】:

以上是关于UITableView没有窗口iOS13时的UITableViewAlertForLayoutOutsideViewHierarchy的主要内容,如果未能解决你的问题,请参考以下文章

UISearchDisplayController 不再隐藏其下方的完整 UITableView

iOS:重新加载 UITableView 后滑动显示数据

[IOS] - UITableView 与 UICollectionView API及其使用

iOS Storyboard约束详解

Firebase 与 Swift 3.0 UITableView

将 UIButton 放入 UITableView