EditDistance 是为 UITableView 和 UICollectionView 提供的增量更新工具之一。
以下展示了如何使用这个库更新 UI。它生成随机条目,并逐一更新它们的 UI。
UITableView | UICollectionView |
---|---|
![]() |
![]() |
是什么意思?
这个库将更新 UITableView 和 UICollectionView 的过程管道化。增量更新它们非常困难,因为 iOS 程序员需要管理两个 DataSource 之间的差异。
如果您更新 DataSource 的条目
// dataSource has ["Francis Elton", "Stanton Denholm", "Arledge Camden", "Farland Ridley", "Alex Helton"]
var nextDataSource = dataSource
// insertion and deletion to data source
nextDataSource.remove(at: 2)
nextDataSource.insert("Woodruff Chester", at: 1)
nextDataSource.insert("Eduard Colby", at: 3)
典型代码
// You have to update UITableView according to array's diff.
dataSource = nextDataSource
tableView.beginUpdates()
tableView.deleteRows(at: [IndexPath(row: 2, section: 0)], with: .fade)
tableView.insertRows(at: [IndexPath(row: 1, section: 0), IndexPath(row: 3, section: 0)], with: .fade)
tableView.endUpdates()
EditDistance 承担这个任务
// You don't need to write insertion and deletion.
let container = dataSource.diff.compare(to: nextDataSource)
dataSource = nextDataSource
tableView.diff.reload(to: container)
您只需要创建更新的数组。
您不需要管理如何增量更新。这使您可以管道化这个过程。
它是如何工作的?
EditDistance 计算差异,并将其转换为 UITableView 或 UICollectionView 的增量更新。
差异基于 编辑距离算法。计算方法有很多,几乎都近似运行在线性时间内。
- 动态规划 (O(NM))
- Mayer 算法 (O(ND))
- Wu 算法 (O(NP))
- 等等。
N 和 M 是每个数组序列的大小。D 是编辑距离,P 是删除数量。
在我们的背景下,Wu 算法似乎是最好的算法。当您的应用程序有很多项目并且只添加(或删除)几个项目时,它的性能优于其他算法。(例如,自动分页,访问历史和通知)
优缺点
本库中的计算不总是合理地更新UI。我建议你的应用在子线程中计算编辑距离,并在主线程中更新UI。
特色
- 无需手动计算diff。
- 你可以选择你喜欢的任何diff算法。
- 不再需要调用reloadRows(at:with:) 和 performBatchUpdates(_:completion:)了。
- 增量更新的最小实现。
需求
- iOS 8.0+
- Xcode 8.1+
- Swift 3.0+
安装
Carthage
- 从Homebrew安装Carthage
> ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
> brew update
> brew install carthage
- 移动你的项目目录并创建Cartfile
> touch Cartfile
- 在Cartfile中添加以下行
github "kazuhiro4949/EditDistance"
- 创建框架
> carthage update --platform iOS
- 在Xcode中,转到“通用 > 编译阶段 > 链接的框架和库”
- 将框架添加到你的项目中
- 添加一个新的运行脚本并将以下代码放入其中
/usr/local/bin/carthage copy-frameworks
- 在“输入文件”中点击“+”并添加框架路径
$(SRCROOT)/Carthage/Build/iOS/EditDistance.framework
- 在源文件中编写导入语句
Import EditDistance
CocoaPods
- 安装CocoaPods
> gem install cocoapods
> pod setup
- 创建Podfile
> pod init
- 编辑Podfile
# Uncomment this line to define a global platform for your project
platform :ios, '8.0' # add
use_framework! # add
target 'MyAppName' do
pod 'EditDistance' # add
end
target 'MyAppTests' do
end
target 'MyAppUITests'
- 安装
> pod install
打开.xcworkspace文件
使用
计算两个数组的差异
一维数组
1. 准备两个数组。
let current = ["Francis", "Woodruff", "Stanton"]
let next = ["Francis", "Woodruff", "Stanton", "Eduards"]
实例。
2. 从数组中调用diff创建EditDistanceProxylet proxy = current.diff // => EditDistanceProxy<String>
3. 实例具有compareTo用来与下一个数组计算差异。
let container = proxy.compare(to: next) // => EditDistanceContainer<String>
二维数组
1. 准备两个数组。
let current = [["Francis", "Woodruff"], ["Stanton"]]
let next = [["Francis", "Woodruff"], ["Stanton", "Eduard"]]
2. 实例化 EditDistance 对象
let editDistance = EditDistance(from: current, to: next) // => EditDistance<String>
3. 该实例具有 compare(to:) 来计算与下一个数组的差异。
let container = editDistance.calculate() // => EditDistanceContainer<String>
定制算法
到预设算法对象
let container = current.diff.compare(to: next, with: DynamicAlgorithm())
到闭包
// implement algorithm
let algorithm = AnyEditDistanceAlgorithm { (from, to) -> EditDistanceContainer<String> in
//...
//...
}
let container = current.diff.compare(to: next, with: algorithm)
创建一个新的算法类。
//implements protocol
public struct Wu<T: Equatable>: EditDistanceAlgorithm {
public typealias Element = T
public func calculate(from: [[T]], to: [[T]]) -> EditDistanceContainer<T> {
//...
//...
}
}
对UITableView的增量更新
1. 计算两个数组之间的差异
let nextDataSource = ["Francis Elton", "Woodruff Chester", "Stanton Denholm", "Eduard Colby", "Farland Ridley", "Alex Helton"]
let container = dataSource.diff.compare(to: nextDataSource)
2. 更新数据源和UI
dataSource = nextDataSource
tableView.diff.reload(with: container)
如果你不再使用这个库
ataSource = nextDataSource
// tableView.diff.reload(with: container)
tableView.reloadData()
就是这样!
性能
该库推荐使用吴氏算法。实际速度取决于两个数组之间差异的数量和元素"=="操作的成本。以下是一些参考平均速度,它们是在iPhone7、iOS 10.2模拟器和启用“整个模块优化选项”设置的情况下执行的。示例数组由随机UUID字符串组成。
- 从100个项目到120个项目(增加20项),平均:0.001秒
- 从100个项目到100个项目(增加10项和删除10项),平均:0.001秒
- 从100个项目到200个项目(增加100项),平均:0.001毫秒
- 从100个项目到100个项目(增加50项和删除50项),平均:0.001秒
- 从1000个项目到1050个项目(增加50个),平均:0.003秒
- 从1000个项目到1000个项目(增加25个,删除25个),平均:0.003秒
- 从1000个项目到1200个项目(增加200个),平均:0.003秒
- 从1000个项目到1000个项目(增加100个,删除100个),平均:0.008秒
- 从10000个项目到10100个项目(增加100个),平均:0.031秒
- 从10000个项目到10000个项目(增加50个,删除50个),平均:0.032秒
- 从10000个项目到12000个项目(增加2000个),平均:0.033秒
- 从10000个项目到10000个项目(增加1000个,删除1000个),平均:0.055秒
测试用例在这里。您可以用它们进行重新审视。
类设计
- EditDistance 是一个用于计算两个输入数组之间的 EditDistanceAlgorithm 的导向器。
- AnyEditDistanceAlgorithm 是一个用于 EditDistanceAlgorithm 的类型擦除结构。
- EditDistanceContainer 是一个容器,用于连接算法的结果和视图的更新。
- EditScriptConverter 是一个命名空间,用于使用对 UIKit 类的一些扩展。
- EditScriptConverterProxy 是 UITableView 和 UICollectionView 的代理。它有方法来更新项目。
许可证
版权所有(c)2017 Kazuhiro Hayashi
在此特此授予任何获得本软件及其相关文档副本(“软件”)的人无限制地在软件中处理软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本,并允许向提供软件的人授予这样做,前提是以下条件
上述版权声明和本许可声明应包括在软件的所有副本或主要部分中。
本软件按“现状”提供,不提供任何形式的保证,无论是明示的还是隐含的,包括但不限于适销性、适用于特定目的和无侵权性的保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任承担责任,无论责任基于合同、侵权或其他原因,是否源自、由或与软件或其使用或任何其他处理有关。