当您的数据源是结果对象时,如何在 table 视图中移动行?

How to move rows in a table view when your data source is a Results object?

我有一个 table 视图,它从结果对象中提取数据。但是,因为您不能在 Results 对象中四处移动项目,所以我必须使用 List 对象在 table 视图中四处移动行。

但是如果将数据从结果转换为列表对象(将列表对象用于 table 视图),列表对象将不再是反应性的。这意味着它只包含结果对象中数据的副本。

所以我的问题是: 当您的 table 视图从结果对象中提取数据时,您如何在 table 视图中移动行?

您可以尝试对数组进行排序。

var results = [Results]()

所以在results之后都插入了。尝试像这样对它们进行排序。

var filteredResults: [Result] {
    results.sorted(by: { [=11=].id < .id })
}

这会将它们从最小 ID 到最大 ID 排序。

我的示例结果结构

struct Result {
    let id: Int
}

然后在您的 DataSource 中使用这个数组而不是第一个。它将以相同的顺序显示在 table 视图中。

首先,您必须像这样定义 Results 结构。

struct Results {
    var items: [Int]

    mutating func move(from: Int, to: Int) {
        let temp = items[from]
        items[from] = items[to]
        items[to] = temp
    }
}

在 UITableViewDelegate 中,每次移动项目时,都会调用此方法并在其中处理数据更改

func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {
    results.move(from: sourceIndexPath.item, to: proposedDestinationIndexPath.item)
    return proposedDestinationIndexPath
}

您需要为模型添加索引 属性。要么,要么您需要定义一个新的领域 class,它有一个索引 属性 和对您要定义的对象的引用。我会推荐第一个,因为它更简单。这是执行此操作的一种方法。

我会实施一个协议,以便您可以将相同的逻辑应用于其他模型。

protocol IndexableObject: AnyObject {
    var index: Int { get set }
}

现在遵守协议。

class MovingObject: Object, IndexableObject {
    @objc dynamic var name: String = ""
    @objc dynamic var index: Int = 0
}

如果您将扩展添加到结果应用程序,将会很有帮助。基本上你想要做的是提取索引在 [fromIndex, toIndex] 范围内的元素并将它们存储在一个数组中,这样你就可以在执行“移动”后更新索引。过滤器确保您只更新必要的索引,而不是结果集中的所有元素。

extension Results where Element: IndexableObject {

    func moveObject(from fromIndex: Int, to toIndex: Int) {

        let min = Swift.min(fromIndex, toIndex)
        let max = Swift.max(fromIndex, toIndex)

        let baseIndex = min

        var elementsInRange: [Element] = filter("index >= %@ AND index <= %@", min, max)
            .map { [=12=] }

        let source = elementsInRange.remove(at: fromIndex - baseIndex)
        elementsInRange.insert(source, at: toIndex - baseIndex)

        elementsInRange
            .enumerated()
            .forEach { (index, element) in
                element.index = index + baseIndex
            }
    }
}

你的 UIableViewController 中的用法会像这样。

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

    let realm = try! Realm()

    try! realm.write {
        objects.moveObject(from: sourceIndexPath.row, to: destinationIndexPath.row)
    }
}

只需确保您按索引排序,link 您的对象在通知块中一直到 UITableView。您应该在 Realm 的在线文档中找到大量关于如何完成此操作的信息。

lazy var objects: Results<MovingObject> = {
    let realm = try! Realm()
    return realm.objects(MovingObject.self)
        .sorted(by: \.index, ascending: true)
}()

func observeObjects() {
    notificationToken = objects.observe { [weak self] change in
        self?.processChange(change)
    }
}