ConfigableCollectionView 1.0.2

ConfigableCollectionView 1.0.2

mikun维护。



  • mikun

ConfigableCollectionView

中文文档

以类似于iOS 13中新引入的DataSource的方式创建CollectionView

示例基于Apple的ImplementingModernCollectionViews。

优势

覆盖率超过90%

支持更多iOS版本

UICollectionViewDiffableDataSource:iOS 13必备

可配置UICollectionView: 需要 iOS 9,但技术上支持自 iOS 6 以来的所有 iOS 版本

更安全

UICollectionViewDiffableDataSource: 一旦添加重复对象就会崩溃,即使在发布版,有时在某些 iOS 版本上也会发生

ConfigableCollectionView : 仅在调试时添加重复对象时断言

更简单

NSDiffableDataSourceSectionSnapshot 和 NSDiffableDataSourceSnapshot 没有区别

单元格点击基于 hittest,而不是 cell.bounds(这就是 UICollectionView 的工作方式),因此您可以通过覆盖视图的 hittest 来确保您的点击操作正常工作。

支持多种项目类型和分区

UICollectionViewDiffableDataSource : 仅支持一种分区类型和一种项目类型

ConfigableCollectionView: 支持多个项目类型和分区

用法

初始化

用于特定项目类型

let collectionView = CollectionView<Section, Item>(layout: generateLayout())

支持多种各项类型和多种区域类型

let collectionView = CollectionView<Any, Any>(layout: generateLayout())

注册

用于特定项类型

collectionView.register(
  view: { // create view for reuse
    UICollectionViewListCell()
  },
  .config { // config the UICollectionViewListCell with Item
    let cell = $0.view
    let item = $0.data
    var contentConfiguration = cell.defaultContentConfiguration()
    contentConfiguration.text = item.title
    contentConfiguration.textProperties.font = .preferredFont(forTextStyle: .headline)
    cell.contentConfiguration = contentConfiguration

    let disclosureOptions = UICellAccessory.OutlineDisclosureOptions(style: .header)
    cell.accessories = [.outlineDisclosure(options:disclosureOptions)]
    cell.backgroundConfiguration = UIBackgroundConfiguration.clear()
  },
  .when { // Optional, deciding when to use this type of view if need
    !$0.data.subitems.isEmpty
  }
)

collectionView.register(
  view { // create normal view for reuse
    ContentView()
  },
  .config(map: \.title) { // config the ContentView with Item.title, configurationState: UICellConfigurationState(introduced in iOS 14, no useful when you using UICollectionViewCell as View) 
    $0.view.data = $0.data
    if $0.configurationState.isHighlighted {
    	$0.view.backgroundColor = .red
    }
    ...
  },
  .flowLayoutSize { _ in // Optional, deciding the size in flow layout
    CGSize(width: 100, height: 100)
  },
  .tap { _ in  // Optional, deciding what to do after tap the view
    Router.push( ... )
  }
)
...

使用多种项类型

collectionView.register(
  dataType: Int.self,
  view {
    ContentView()
  },
  .config {
    $0.view.data = $0.data
  }
)
collectionView.register(
  dataType: String.self,
  view {
    UILabel()
  },
  .config {
    $0.view.text = $0.data
  }
)

视图封装是支持在视图创建时配置对象弱引用的视图构建器,例如

collectionView.register(
  dataType: Int.self,
  view { [weak self] in
    if let color = self?.color {
      ContentView(color: color)
    }
  },
  .config {
    $0.view.data = $0.data
  }
)

注意:如果您使用UICollectionView的子类,则不会使用视图封装创建视图,而是使用UICollectionView.dequeue。

设置数据,与UICollectionViewDiffableDataSource非常相似

类似

collectionView.dataManager.appendSections([Section.main])
collectionView.dataManager.appendItems(mountains)
or just
collectionView.dataManager.applyItems(mountains, updatedSection: Section.main)

在appendChildItems中支持recursivePath

collectionView.dataManager.appendChildItems(menuItems, to: nil, recursivePath: \.subitems)
is equal to:
func addItems(_ menuItems: [OutlineItem], to parent: OutlineItem?) {
    collectionView.dataManager.appendChildItems(menuItems, to: parent)
    for menuItem in menuItems where !menuItem.subitems.isEmpty {
        addItems(menuItem.subitems, to: menuItem)
    }
}
addItems(menuItems, to: nil)

在同一个区域使用多种项类型

let numbers: [Int]
let stings: [String]

collectionView.dataManager.appendItems(numbers)
collectionView.dataManager.appendItems(stings)

collectionView.register(
  dataType: Int.self,
  view {
    NumberView()
  }
)
collectionView.register(
  dataType: String.self,
  view {
    UILabel()
  }
)

动画和更新完成回调,在所有数据处理函数之后使用.on(animatingDifferences: completion),例如:

collectionView.dataManager.appendItems(stings)
.on(animatingDifferences: false, completion: { print("appended") })

有关更多使用信息,请查阅《ImplementingModernCollectionViews》的差别。

注意

为了支持旧版本iOS,在iOS 13以上使用NSDiffableDataSourceSnapshot,而在iOS 13以下直接将数据放入CustomUICollectionViewDataSource,以减少重新创建NSDiffableDataSourceSnapshot实例的数量,因此CollectionView的刷新细胞是异步的。为了避免这种情况,您可以调用reloadImmediately()。

您可以使用自己的UICollectionViewDelegate(某些委托函数不会调用),但不能重置UICollectionViewDataSource。

已知问题

由于性能问题,无法实现添加子项的过滤,所以仍然直接添加到NSDiffableDataSourceSectionSnapshot中,如果在发布时添加重复对象,则会崩溃。

因为它将重新创建NSDiffableDataSourceSnapshot实例,如果在基于iOS 14的NSDiffableDataSourceSnapshot之上的数据处理函数中使用,则不会保存展开状态,如果更改数据,则会折叠所有项。

安装

pod 'ConfigableCollectionView'

待办事项列表

  • 高效的添加子项的过滤器。
  • 区分iOS 13以上数据处理函数的成就,以解决重新创建NSDiffableDataSourceSnapshot实例的问题。
  • 移除Proxy.m以支持Swift包管理器或等待它支持.m文件。
  • TVOS支持。
  • 文档补充。