当UITableView没有窗口iOS13时的UITableViewAlertForLayoutOutsideViewHierarchy

时间:2019-09-27 07:49:31

标签: ios uitableview nsfetchedresultscontroller ios13

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

下面的代码在以前的版本中工作正常。

PS:任何一种beginUpdatesreloadRowsAtIndexPaths:withRowAnimation:insertSections:withRowAnimation:endUpdates都会引起此警告。

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

  

2019-09-27 09:40:42.849128 + 0200 xxx [63595:9762090] [TableView]警告   仅一次:通知UITableView布置其可见单元格和其他   不在视图层次结构中的内容(表视图或   其超级视图尚未添加到窗口中)。这可能会导致错误   强制表格视图内的视图加载和执行布局而无需   准确的信息(例如表格视图范围,特征收集,布局   边距,安全区域插图等),还会导致不必要的   由于额外的布局传递而导致的性能开销。做一个象征   UITableViewAlertForLayoutOutsideViewHierarchy的断点以捕获   在调试器中查看此情况,并查看导致此情况的原因,因此您可以   如果可能,请完全避免执行此操作,或者将其推迟到表格中   视图已添加到窗口。表格视图:   层=; contentOffset:{0,-64};   contentSize:{375,3432}; AdjustedContentInset:{64,0,0,0};   dataSource:>

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

2 个答案:

答案 0 :(得分:0)

  

PS:任何类型的beginUpdates,reloadRowsAtIndexPaths:withRowAnimation:,insertSections:withRowAnimation:,endUpdates都会引起此警告。

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

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

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

答案 1 :(得分:0)

您可以尝试下面的代码,即检查窗口并调用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];
}

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