Changeset

Changeset – 非常棒的小项目
Joel Levin

这是尝试实现 Dave DeLong 的文章 Edit distance and edit steps 中概述的解决方案。

Changeset 描述了将一个 Equatable 元素的 Collection 转换为另一个 Collection 所需的最小编辑操作。

它的主要目的是与 UITableViewUICollectionView 数据源结合使用,通过检测两个数据集之间的添加、删除、替换和移动操作。但它也可以用于计算两个数据集之间更通用的更改。

用法

以下代码计算了规范示例的最小编辑操作,将 String 集合从 “kitten” 转换为 “sitting”

let changeset = Changeset(source: "kitten", target: "sitting")

print(changeset)
// 'kitten' -> 'sitting':
//     replace with s at offset 0
//     replace with i at offset 4
//     insert g at offset 6

以下断言将会成功

let edits = [
    Changeset<String>.Edit(operation: .substitution, value: "s", destination: 0),
    Changeset<String>.Edit(operation: .substitution, value: "i", destination: 4),
    Changeset<String>.Edit(operation: .insertion, value: "g", destination: 6),
]
assert(changeset.edits == edits)

如果您不想要 Changeset 本身的开销(它还会存储源集合和目标集合),您可以直接调用 edits(此处使用了来自 Apple 示例数据,出自 iOS Table View Programming Guide

let source = ["Arizona", "California", "Delaware", "New Jersey", "Washington"]
let target = ["Alaska", "Arizona", "California", "Georgia", "New Jersey", "Virginia"]
let edits = Changeset.edits(from: source, to: target)

print(edits)
// [insert Alaska at offset 0, replace with Georgia at offset 2, replace with Virginia at offset 4]

请注意,Changeset 使用偏移量而不是索引来引用集合中的元素。这主要是因为 Swift 集合不保证使用从零开始的整数索引。有关更多详细信息,请参阅 issue #37 中的讨论。

UIKit 集成

偏移量值可以直接在 UITableView 上的 beginUpdates/endUpdatesUICollectionView 上的 performBatchUpdates 的动画块中使用,因为 Changeset 遵循 Apple 指南中 批量插入、删除和重新加载行和节 下解释的原则。

简而言之;首先相对于源集合进行所有删除和替换,然后相对于结果集合进行插入。移动只是一个删除后跟一个插入。

在 iOS 框架中,包含了两个便捷扩展(一个在 UITableView 上,一个在 UICollectionView 上),使动画表格/集合视图更新变得轻而易举。只需调用 update,就像这样

tableView.update(with: changeset.edits)

自定义比较器

默认情况下,Changeset 使用 == 来比较元素,但您可以编写自己的比较器,如下所示,其中 “a” 的出现始终会触发更改

let alwaysChangeA: (Character, Character) -> Bool = {
    if $0 == "a" || $1 == "a" {
        return false
    } else {
        return $0 == $1
    }
}
let changeset = Changeset(source: "ab", target: "ab", comparator: alwaysChangeA)

因此,changeset 将包含将 “a” 替换为另一个 “a” 的操作

let expectedEdits: [Changeset<String>.Edit] = [Changeset.Edit(operation: .substitution, value: "a", destination: 0)]
assert(changeset.edits == expectedEdits)

一种可能的用途是当 UITableViewUICollectionView 中的单元格在更改时不应动画时。

测试应用

Xcode 项目还包含一个目标,用于说明在应用程序中的用法

Test App

这使用上面提到的扩展来基于 Changeset 的编辑操作来动画过渡。

许可证

本项目根据 MIT 许可证 提供。
版权所有 © 2015-18, Joachim Bondo。请参阅 LICENSE 文件。