KarrotListKit

欢迎使用我们的 Karrot 列表框架。这个强大的工具使用 UIKit 开发,但其设计方式类似于声明式 UI API,可以平滑过渡到 SwiftUI 并降低迁移成本。

我们的框架构建于优化的 diffing 算法之上,这得益于它对 DifferenceKit 的依赖。这确保了高性能和快速的列表渲染,即使在处理大量数据变更时也能保证应用程序流畅运行。

该 API 设计简洁直观,允许快速开发,同时不牺牲质量或功能。这意味着您可以减少与复杂代码的斗争,而将更多时间用于创建完美的用户体验。

安装

您可以使用 Swift Package Manager 通过以下步骤安装此框架:

  1. 打开 Xcode,并导航到 Project -> Package dependencies -> Add Package Dependency (+)
  2. 在搜索栏中,输入此仓库的 URL:https://github.com/daangn/KarrotListKit
  3. 指定您想要使用的版本。您可以选择使用最新版本或特定版本。
  4. 单击 NextFinish 完成安装。 将包成功添加到您的项目后,将框架导入到您想要使用它的文件中。
import KarrotListKit

现在您可以开始使用 KarrotListKit 框架了。

开始使用

请参阅托管在 Swift Package Index 上的 KarrotListKit DocC 文档

CollectionViewAdapter

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 (组件)

Component (组件) 是框架中最小的单元。它允许声明式地表示要在屏幕上显示的数据和操作。 我们不再需要依赖 UICollectionViewCellUICollectionReusableView,可以编写基于组件的代码。 该组件具有与 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)
  }
}

Presentation (展示)

我们使用 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
}

Sizing (尺寸)

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
}

Pagination (分页)

KarrotListKit 提供了一个易于使用的界面,用于在加载下一页数据时处理分页。 传统上,这可能在 scrollViewDidScroll 方法中实现,但 KarrotListKit 为此目的提供了一个更结构化的机制。

List 提供了一个 onReachEnd 修饰符,当到达列表末尾时会调用它。 此修饰符可以附加到 List

List(sections: [])
  .onReachEnd(
    offset: .absolute(100.0),
    handler: { _ in
      // Closure Trigger when reached end of list.
    }
  )

第一个参数 offsetReachedEndEvent.OffsetFromEnd 类型的枚举,允许用户设置触发条件。

提供了两个选项:

默认情况下,该值设置为 .relativeToContainerSize(multiplier: 2.0),这将在滚动位置位于距离列表视图末尾两倍高度之内时触发事件。

第二个参数 handler 是回调处理程序,当到达列表末尾时执行异步操作。

Prefetching (预取)

提供资源预取 API 以提高滚动性能。 CollectionViewAdapter 遵循 UICollectionViewDataSourcePrefetching。 该框架提供 ComponentResourcePrefetchableCollectionViewPrefetchingPlugin 协议以实现兼容性。

以下是图像预取的示例代码。

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")
    ]
  }
}

Contributing (贡献)

我们热烈欢迎并感谢您对该项目的任何贡献! 欢迎提交 pull requests 以增强此项目的功能。

License (许可证)

本项目采用 Apache License 2.0 许可。 有关详细信息,请参阅 LICENSE