当您的数据源是 Results 对象时,如何在表格视图中移动行?

Posted

技术标签:

【中文标题】当您的数据源是 Results 对象时,如何在表格视图中移动行?【英文标题】:How to move rows in a table view when your data source is a Results object? 【发布时间】:2021-10-29 18:47:32 【问题描述】:

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

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

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

【问题讨论】:

如果您移动行,结果将自动更新。因此,只需在结果中添加一个观察者来刷新 tableView,当 Realm 更新时,这一切都会自动发生。您还可以在观察闭包中执行手动删除、插入和修改。见React To Changes 【参考方案1】:

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

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

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  $0 

        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)
    

只需确保按索引排序并将对象链接到通知块中的 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)
    

【讨论】:

【参考方案2】:

首先,你必须像这样定义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

【讨论】:

这不正确......好吧,它是正确的,但不适用于所提出的问题。 OP 正在使用 Realm Results 对象,该对象不可直接修改,而不是结构。【参考方案3】:

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

var results = [Results]()

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

var filteredResults: [Result] 
    results.sorted(by:  $0.id < $1.id )

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

我的示例结果结构

struct Result 
    let id: Int

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

【讨论】:

小心这个。在 Objc Realm 对象上运行 Swift 函数可以将结果与 Realm 分离,因此它们不会自动更新。更好的选择是使用 Realm .sorted(byKeyPath: "name" 函数进行排序。

以上是关于当您的数据源是 Results 对象时,如何在表格视图中移动行?的主要内容,如果未能解决你的问题,请参考以下文章

当您的应用程序在前台时,如何在 ios 10 中生成本地通知?当您的应用程序在前台时,它就不起作用

怎么把百度推广的各项统计数据导出到excel表

当您的方法签名不允许抛出异常时如何抛出异常?

使用 gwt dto 时如何避免使用异步类?

删除核心数据对象和表格单元

如何在Javascript中动态隐藏对象