NSFetchedResultsController 添加新记录时如何防止 UITableView 向上滚动?

Posted

技术标签:

【中文标题】NSFetchedResultsController 添加新记录时如何防止 UITableView 向上滚动?【英文标题】:How to prevent from scrolling UITableView up when NSFetchedResultsController add new record? 【发布时间】:2015-10-08 09:44:34 【问题描述】:

一旦我向 CoreData 添加新评论,我的 UITableView 就会向上滚动。 NSFetchedResultsController 知道它,将此注释放在表格底部,并将表格滚动到顶部。正常吗?

我的例子:

就在我添加评论之前:

就在我添加评论之后(不是预期的行为):

应该是这样的(手动刷之后):

这可能与以下情​​况有关:

    这是我的NSFetchedResultsController 的排序描述符:

    NSSortDescriptor(key: "createdAt", ascending: false) //from the latest to the oldest
    

    但我需要为反向索引路径显示我的 cmets(最新的在最底部)。换句话说,当我需要使用indexPath 时,我会在任何地方使用:

    private func reversedIndexPathForIndexPath(indexPath: NSIndexPath) -> NSIndexPath 
        return NSIndexPath(forRow: fetchedResultsController.fetchedObjects!.count - indexPath.row - 1, inSection: 0)
    
    

NSFetchedResultsControllerDelegate:

//MARK: - NSFetchedResultsControllerDelegate

func controllerWillChangeContent(controller: NSFetchedResultsController) 
    self.tableView.beginUpdates()


func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) 

    switch type 
    case .Insert:
        if let newIndexPath = newIndexPath 
            tableView.insertRowsAtIndexPaths([reversedIndexPathForIndexPath(newIndexPath)], withRowAnimation: .Fade)
        
    case .Delete:
        if let indexPath = indexPath 
            tableView.deleteRowsAtIndexPaths([reversedIndexPathForIndexPath(indexPath)], withRowAnimation: .Fade)
        
    case .Update:
        if let indexPath = indexPath 
            tableView.reloadRowsAtIndexPaths([reversedIndexPathForIndexPath(indexPath)], withRowAnimation: .Fade)
        
    case .Move:
        if let indexPath = indexPath, let newIndexPath = newIndexPath 
            tableView.deleteRowsAtIndexPaths([reversedIndexPathForIndexPath(indexPath)], withRowAnimation: .Fade)
            tableView.insertRowsAtIndexPaths([reversedIndexPathForIndexPath(newIndexPath)], withRowAnimation: .Fade)
        
    


func controllerDidChangeContent(controller: NSFetchedResultsController) 
    tableView.endUpdates()

这与我的问题有关吗?

【问题讨论】:

在添加新记录之前禁用tableview的滚动 @PiyushPatel self.tableView.scrollEnabled = false,仍然移到顶部 使用:tableView.alwaysBounceVertical = NO. 您是否在 FRC 委托的 controllerDidChangeContent: 方法中的表视图上调用 reloadData @bteapot,不,我应该吗? 【参考方案1】:

修改后的答案:

我能够通过以下步骤重现此故障:

    UITableView,其 rowHeight 设置为 UITableViewAutomaticDimension 和非零 estimatedRowHeight。 表格滚动到最底部,最后一个单元格在底部边缘完全可见。 将新单元格插入到最后一个单元格的下方会导致单元格的视觉偏移几个点(在我的设置中,它从 5 到 40 不同)。

这种行为的根源需要进一步调查。

目前唯一的解决方案是不使用UITableViewAutomaticDimension 并从tableView:heightForRowAtIndexPath: 返回行高。

顺便说一下,倒排索引路径的实现有一个重大缺陷。当 FRC 调用它的委托方法controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 时,所有删除索引路径都属于更改前数据集,所有插入索引路径都属于更改后数据集。当你在controllerWillChangeContent:controllerDidChangeContent: 之间时,fetchedResultsController.fetchedObjects 将包含更改后的数据集,我想(不过需要检查)。

【讨论】:

不,不能这样。这种方式只有在我想显示所有消息时才有效。如果我只需要显示 10 条最新消息怎么办? 然后进行降序。现在关于跳到顶部:您使用估计的行高吗? 是的,我的 tableView 使用UITableViewAutomaticDimension 这可能是问题所在。你能试着把它关掉,然后从tableView:heightForRowAtIndexPath:返回一些常量,比如100 不,因为我的单元格的高度不同,并且取决于文本的长度。【参考方案2】:

我发现了一些对我有用的东西。 我遇到了同样的问题,我的估计行高度是 33,我的所有单元格都大于 33。

我刚刚将我的估计行高度更改为我的最大单元格高度(或大于您的最大单元格高度)

    self.table.estimatedRowHeight = 310
    self.table.rowHeight = UITableViewAutomaticDimension

就像一个魅力,现在所有单元格都自动调整大小,当核心数据执行更新/删除/插入时,uitableview 不会滚动到顶部或底部

【讨论】:

以上是关于NSFetchedResultsController 添加新记录时如何防止 UITableView 向上滚动?的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?