欢迎使用我们的 Karrot 列表框架。这个强大的工具使用 UIKit 开发,但其设计方式类似于声明式 UI API,可以平滑过渡到 SwiftUI 并降低迁移成本。
我们的框架构建于优化的 diffing 算法之上,这得益于它对 DifferenceKit 的依赖。这确保了高性能和快速的列表渲染,即使在处理大量数据变更时也能保证应用程序流畅运行。
该 API 设计简洁直观,允许快速开发,同时不牺牲质量或功能。这意味着您可以减少与复杂代码的斗争,而将更多时间用于创建完美的用户体验。
您可以使用 Swift Package Manager 通过以下步骤安装此框架:
Project
-> Package dependencies
-> Add Package Dependency (+)
。https://github.com/daangn/KarrotListKit
。Next
和 Finish
完成安装。 将包成功添加到您的项目后,将框架导入到您想要使用它的文件中。import KarrotListKit
现在您可以开始使用 KarrotListKit 框架了。
请参阅托管在 Swift Package Index 上的 KarrotListKit DocC 文档。
CollectionViewAdapter
对象充当 UIColletionView
逻辑和 KarrotListKit 逻辑之间的适配器,封装了框架的核心实现逻辑。
private let configuration = CollectionViewAdapterConfiguration()
private let layoutAdapter = CollectionViewLayoutAdapter()
private lazy var collectionViewAdapter = CollectionViewAdapter(
configuration: configuration,
collectionView: collectionView,
layoutAdapter: layoutAdapter
)
private lazy var collectionView = UICollectionView(
frame: .zero,
collectionViewLayout: UICollectionViewCompositionalLayout(
sectionProvider: layoutAdapter.sectionLayout
)
)
Component (组件) 是框架中最小的单元。它允许声明式地表示要在屏幕上显示的数据和操作。 我们不再需要依赖 UICollectionViewCell
和 UICollectionReusableView
,可以编写基于组件的代码。 该组件具有与 UIViewRepresentable
非常相似的接口。 这种相似性使我们能够降低将来迁移到 SwiftUI
的成本。
struct ButtonComponent: Component {
typealias Content = Button
typealias ViewModel = Button.ViewModel
typealias Coordinator = Void
let viewModel: ViewModel
init(viewModel: ViewModel) {
self.viewModel = viewModel
}
func renderContent(coordinator: Coordinator) -> Button {
Button()
}
func render(in content: Button, coordinator: Coordinator) {
content.configure(viewModel: viewModel)
}
var layoutMode: ContentLayoutMode {
.flexibleHeight(estimatedHeight: 44.0)
}
}
我们使用 List / Section / Cell 来表示列表 UI。 不仅如此,我们还可以使用修饰符来映射动作和布局。
let list = List {
Section(id: "Section1") {
Cell(
id: "Cell1",
component: ButtonComponent(viewModel: .init(title: $0.rawValue))
)
Cell(
id: "Cell2",
component: ButtonComponent(viewModel: .init(title: $0.rawValue))
)
.didSelect { context in
// handle selection
}
.willDisplay { context in
// handle displaying
}
}
.withHeader(ButtonComponent(viewModel: .init(title: "Header")))
.withFooter(ButtonComponent(viewModel: .init(title: "Footer")))
.withSectionLayout(.vertical(spacing: 12.0))
}
collectionViewAdapter.apply(
list,
animatingDifferences: true
) {
// after completion
}
View 的大小实际上是在 View 显示在屏幕上时调整的,这可以通过 sizeThatFits
进行调整。 在此之前,组件可以将其自身的大小表示为估计值。
struct ButtonComponent: Component {
typealias Content = Button
// ...
var layoutMode: ContentLayoutMode {
.flexibleHeight(estimatedHeight: 44.0)
}
}
final class Button: UIControl {
// ...
override func sizeThatFits(_ size: CGSize) -> CGSize {
// return size of a Button
}
}
SectionLayout 与 UICollectionViewCompositionalLayout
紧密耦合,提供自定义界面以返回 NSCollectionLayoutSection
。
Section(id: "Section1") {
// ...
}
.withSectionLayout { [weak self] context -> NSCollectionLayoutSection? in
// return NSCollectionLayoutSection object
}
KarrotListKit
提供了一个易于使用的界面,用于在加载下一页数据时处理分页。 传统上,这可能在 scrollViewDidScroll
方法中实现,但 KarrotListKit
为此目的提供了一个更结构化的机制。
List
提供了一个 onReachEnd
修饰符,当到达列表末尾时会调用它。 此修饰符可以附加到 List
。
List(sections: [])
.onReachEnd(
offset: .absolute(100.0),
handler: { _ in
// Closure Trigger when reached end of list.
}
)
第一个参数 offset
是 ReachedEndEvent.OffsetFromEnd
类型的枚举,允许用户设置触发条件。
提供了两个选项:
case relativeToContainerSize(multiplier: CGFloat)
: 当用户滚动到内容视图高度的倍数范围内时触发事件。case absolute(CGFloat)
: 当用户滚动到距离末尾的绝对点值范围内时触发事件。默认情况下,该值设置为 .relativeToContainerSize(multiplier: 2.0)
,这将在滚动位置位于距离列表视图末尾两倍高度之内时触发事件。
第二个参数 handler
是回调处理程序,当到达列表末尾时执行异步操作。
提供资源预取 API 以提高滚动性能。 CollectionViewAdapter 遵循 UICollectionViewDataSourcePrefetching
。 该框架提供 ComponentResourcePrefetchable
和 CollectionViewPrefetchingPlugin
协议以实现兼容性。
以下是图像预取的示例代码。
let collectionViewAdapter = CollectionViewAdapter(
configuration: .init(),
collectionView: collectionView,
layoutAdapter: CollectionViewLayoutAdapter(),
prefetchingPlugins: [
RemoteImagePrefetchingPlugin(
remoteImagePrefetcher: RemoteImagePrefetcher()
)
]
)
extension ImagePrefetchableComponent: ComponentRemoteImagePrefetchable {
var remoteImageURLs: [URL] {
[
URL(string: "imageURL"),
URL(string: "imageURL"),
URL(string: "imageURL")
]
}
}
我们热烈欢迎并感谢您对该项目的任何贡献! 欢迎提交 pull requests 以增强此项目的功能。
本项目采用 Apache License 2.0 许可。 有关详细信息,请参阅 LICENSE。