繁体   English   中英

使用NSFetchedResultsController核心数据重新排序TableView单元格 - Swift 3

[英]Reorder TableView Cells With NSFetchedResultsController Core Data - Swift 3

我正在使用NSFetchedResultsController。 我找不到任何直接的教程,为Swift 3提供了它。

所以这就是我到目前为止所做的。 我已经使用NSFetchedResultsController成功填充了我的表,该NSFetchedResultsController从核心数据中提取插入的数据。 我在我的核心数据模型中创建了一个名为orderPosition的属性,该属性声明为Int32 关于在插入时向核心数据添加和保存数据,我没有对此属性进行任何操作。

在我初始化NSFetchedResultsController fetch func中,我修改了我的排序描述符以包含以下代码:

let sortDescriptor = NSSortDescriptor(key: "orderPosition", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]

然后我实现了tableView函数:

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {

    attemptFetch()
    var objects = self.controller.fetchedObjects! 
    self.controller.delegate = nil

    let object = objects[sourceIndexPath.row]
    objects.remove(at: sourceIndexPath.row)
    objects.insert(object, at: destinationIndexPath.row)

    var i = 0
    for object in objects {
        object.setValue(i += 1, forKey: "orderPosition")
    }

    appdel.saveContext()
    self.controller.delegate = self

}

在我的didChange anObject函数的实现中,我包含了.insert.delete.update.move的switch-case。 我的.move案例如下所示:

case.move:
        if let indexPath = indexPath {
            tableView.deleteRows(at: [indexPath], with: .fade)
        }
        if let indexPath = newIndexPath {
            tableView.insertRows(at: [indexPath], with: .fade)
        }
        break

请帮我解决这个问题。 我花了几周时间试图理解这一点。 我已经经历了很多(如果不是全部)堆栈溢出问题。 我从一个实现了一些循环思想,因为我有一个类似的思考过程来处理这个但它没有用。 没有我遇到的Swift 3核心数据教程/视频,这将真正帮助我解决和理解这一点。 我不想放弃这个。

Swift偏离C,因为=,+ =和其他类似赋值的操作返回Void 所以你的行object.setValue(i += 1, forKey: "orderPosition")i设置为一个,但总是将位置设置为0.请改为:

var i = 0
for object in objects {
    i += 1
    object.setValue(i, forKey: "orderPosition")
}

或者如果你想简化你的代码,你可以做一点

for (i, object) in objects.enumerated() {
    object.setValue(i, forKey: "orderPosition")
}

到处走:

    var objects = frc.fetchedObjects!

    // Disable fetchedresultscontroller updates.

    let obj objects.remove(at: indexPath.item)
    objects.insert(obj, at: newIndexPath)

    for (i, obj) in objects.enumerated() {
        obj.orderIndex = Int32(i)
    }

    // Enable fetchedresultscontroller updates.

    context.saveContext()

TLDR:禁用fetchedresultscontroller的更新导致其他事情崩溃,或使用不会崩溃的解决方法。

关于为什么要锁定我的全部故事。

禁用的东西是因为tableview已经移动了单元格。 如果你然后设置orderIndex,它将尝试再次移动,这可能会像你一样在你身上崩溃。

我的解决方法是没有.move案例

    case .update, .move:
        print("update")
        self.reloadRows(at: [indexPath!], with: .automatic)

我不需要,因为我现在只用tableview移动。 如果你想让fetchedresultscontroller移动东西,你必须没有这种情况.update,.move但是自己处理.move

    case .move:
        print("move")
        self.moveRow(at: indexPath!, to: newIndexPath!)

但是不要忘记在下面实施锁定或我的替代方案。

在endUpdates上没有崩溃的另一个解决方法是

    if let toIndexPath = newIndexPath {
        if type == .update && toIndexPath != indexPath!{
            print("it should be a move.")
            print("move")
            self.moveRow(at: indexPath!, to: newIndexPath!)
            return
        }
    }

它检测newIndexPath!= indexPath(旧索引路径)的更新,基本上它检测未被声明为移动的移动并为我修复问题,但这对我来说太奇怪了。

以下是fetchedresultscontrollers对我的orderIndex更改的回答,这可以解释为什么你,想要让fetchedresultscontroller移动东西来更好地实现我的“移动伪装为更新”检测或在更新对象期间禁用对表的更新,这很好,因为桌子已经通过你的移动重新排序了细胞本身。

    from: Optional([0, 2])
    to: Optional([0, 1])
    update
    from: Optional([0, 1])
    to: Optional([0, 0])
    update
    from: Optional([0, 0])
    to: Optional([0, 2])
    move
    ... Assertion failure in ...
    attempt to perform an insert and a move to the same index path (<NSIndexPath: 0xc000000000400016> {length = 2, path = 0 - 2})
    (null)

编辑:

我认为“移动伪装成更新”检测不会产生垃圾,因为它只是重新加载所有细胞。 因此,它通过tableview本身将tableviewcell移动1次,通过检测移动3次(无用)。

为什么fetchedresultscontroller使用这个调用模式? 它可能会疯狂地尝试按顺序获取表,就像更新来自外部并且表需要更新一样。 但是它不知道表格单元格已经由tableview以正确的顺序排列,因此由于某种原因它在super.endUpdates()上崩溃了。

SOO:可能最简单的解决方案是禁用对tableview的更新。 怎么样? 现在不知道,我要睡觉了。

刚刚在我的项目中实现了类似的功能。变得比看起来更容易。

这是您的moveRowAt方法的更新

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {

    // NOT NEEDED, SINCE I ASSUME THAT YOU ALREADY FETCHED ON INITIAL LOAD
    // attemptFetch()

    var objects = self.controller.fetchedObjects! 

    // NOT NEEDED
    //self.controller.delegate = nil

    let object = objects[sourceIndexPath.row]
    objects.remove(at: sourceIndexPath.row)
    objects.insert(object, at: destinationIndexPath.row)

    // REWRITEN BELOW
    //var i = 0
    //for object in objects {
    //    object.setValue(i += 1, forKey: "orderPosition")
    //}

    for (index, item) in items.enumerated() {
        item.orderPosition = index
    }

    appdel.saveContext()
    //self.controller.delegate = self

}

didChange技术上讲,您不需要任何代码。 当您移动项目时,表格会就地更新。 保存上下文时,coreData中的数据会更新。 因此,下次提取数据时,根据您拥有的sortDescriptor,它将按orderPosition排序。

为我工作。 如果您有任何疑问,请与我们联系。

假设核心数据和fetchedResultsController:关于moveRowAt,from和to索引路径行在该部分内。 如果要在各部分之间移动,则需要计算实际行以在数组中移动。 即:如果您要将第三部分中的第二行移动到第一部分中的第二行,您实际上需要知道所有部分中有多少行要删除正确的行并插入到正确的位置。

我使用以下方法来获取正确的行: -

    // Do not trigger delegate methods when changes are made to core data by the user
        fetchedResultsController.delegate = nil
        var fromIndex = fromIndexPath.row
        var toIndex = toIndexPath.row
        //      print ("from row ",fromIndexPath.row)
//work out correct row to remove at based on it's current row and section
        for sectionIndex in 0..<fromIndexPath.section
        {
            fromIndex += fetchedResultsController.sections![sectionIndex].numberOfObjects
        }
            //print ("fromIndex ",fromIndex)
// remove the row at it's old position
       toDoData.remove(at: fromIndex)
//work out the correct row to insert at based on which section it's going to & row in that section
        for sectionIndex in 0..<toIndexPath.section
        {
            toIndex += fetchedResultsController.sections![sectionIndex].numberOfObjects
            //print ("toIndex ",toIndex)
            if sectionIndex == fromIndexPath.section
            {
                toIndex -= 1 // Remember, controller still thinks this item is in the old section
                //print ("-= toIndex",toIndex)
            }
        }
// put the item back into he array at new position
         toDoData.insert(item, at: toIndex)

注意:toDoData是来自fetchedResultsController的记录数组

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM