当您的数据源是结果对象时,如何在 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)
}
}
我有一个 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)
}
}