我如何在 swift 中使用 @resultbuilder 使用 UICollectionView?
How can i use UICollectionView using @resultbuilder in swift?
我想使用 @resultbuilder
在 UIKit 中创建我自己的声明式 UICollectionView,类似于我们在 SwiftUI 中使用 List {}
得到的结果。
我的 @resultBuilder
创建快照如下所示:
@resultBuilder
struct SnapshotBuilder {
static func buildBlock(_ components: ListItemGroup...) -> [ListItem] {
return components.flatMap { [=11=].items }
}
static func buildFinalResult(_ component: [ListItem]) -> NSDiffableDataSourceSectionSnapshot<ListItem> {
var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<ListItem>()
sectionSnapshot.append(component)
return sectionSnapshot
}
}
我还需要使用以下扩展来将 ListItemGroup
传递给 SnapshotBuilder
并得到 [ListItem]
struct ListItem: Hashable {
let title: String
let image: UIImage?
var children: [ListItem]
init(_ title: String, children: [ListItem] = []) {
self.title = title
self.image = UIImage(systemName: title)
self.children = children
}
}
protocol ListItemGroup {
var items: [ListItem] { get }
}
extension Array: ListItemGroup where Element == ListItem {
var items: [ListItem] { self }
}
extension ListItem: ListItemGroup {
var items: [ListItem] { [self] }
}
我的 List
class 看起来像这样:
final class List: UICollectionView {
private enum Section {
case main
}
private var data: UICollectionViewDiffableDataSource<Section, ListItem>!
init(@SnapshotBuilder snapshot: () -> NSDiffableDataSourceSectionSnapshot<ListItem>) {
super.init(frame: .zero, collectionViewLayout: List.createLayout())
configureDataSource()
data.apply(snapshot(), to: .main)
}
required init(coder: NSCoder) {
super.init(coder: coder)!
}
private static func createLayout() -> UICollectionViewLayout {
let layoutConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
return UICollectionViewCompositionalLayout.list(using: layoutConfig)
}
private func configureDataSource() {
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, ListItem> {
(cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.image = item.image
content.text = item.title
}
data = UICollectionViewDiffableDataSource<Section, ListItem>(collectionView: self) {
(collectionView: UICollectionView, indexPath: IndexPath, identifier: ListItem) -> UICollectionViewCell? in
let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
cell.accessories = [.disclosureIndicator()]
return cell
}
}
}
我正在使用 List
这样的
class DeclarativeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
let collectionView = List {
ListItem("Cell 1")
ListItem("Cell 2")
ListItem("Cell 3")
ListItem("paperplane.fill")
ListItem("doc.text")
ListItem("book.fill")
}
collectionView.frame = view.bounds
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(collectionView)
}
}
这似乎有效。我根据数据计数获取单元格行,但单元格是空的,如下所示:
我需要更改什么才能使其正常工作?我的错误在哪里?我可以在 UICollectionView
中定义数据源吗?谢谢你的回答。
您忘记应用配置。
var content = cell.defaultContentConfiguration()
content.image = item.image
content.text = item.title
cell.contentConfiguration = content // this is important
由于 UIListContentConfiguration
是 struct
,当您执行 var content = cell.defaultContentConfiguration()
时,您只会得到它的副本。
我想使用 @resultbuilder
在 UIKit 中创建我自己的声明式 UICollectionView,类似于我们在 SwiftUI 中使用 List {}
得到的结果。
我的 @resultBuilder
创建快照如下所示:
@resultBuilder
struct SnapshotBuilder {
static func buildBlock(_ components: ListItemGroup...) -> [ListItem] {
return components.flatMap { [=11=].items }
}
static func buildFinalResult(_ component: [ListItem]) -> NSDiffableDataSourceSectionSnapshot<ListItem> {
var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<ListItem>()
sectionSnapshot.append(component)
return sectionSnapshot
}
}
我还需要使用以下扩展来将 ListItemGroup
传递给 SnapshotBuilder
并得到 [ListItem]
struct ListItem: Hashable {
let title: String
let image: UIImage?
var children: [ListItem]
init(_ title: String, children: [ListItem] = []) {
self.title = title
self.image = UIImage(systemName: title)
self.children = children
}
}
protocol ListItemGroup {
var items: [ListItem] { get }
}
extension Array: ListItemGroup where Element == ListItem {
var items: [ListItem] { self }
}
extension ListItem: ListItemGroup {
var items: [ListItem] { [self] }
}
我的 List
class 看起来像这样:
final class List: UICollectionView {
private enum Section {
case main
}
private var data: UICollectionViewDiffableDataSource<Section, ListItem>!
init(@SnapshotBuilder snapshot: () -> NSDiffableDataSourceSectionSnapshot<ListItem>) {
super.init(frame: .zero, collectionViewLayout: List.createLayout())
configureDataSource()
data.apply(snapshot(), to: .main)
}
required init(coder: NSCoder) {
super.init(coder: coder)!
}
private static func createLayout() -> UICollectionViewLayout {
let layoutConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
return UICollectionViewCompositionalLayout.list(using: layoutConfig)
}
private func configureDataSource() {
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, ListItem> {
(cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.image = item.image
content.text = item.title
}
data = UICollectionViewDiffableDataSource<Section, ListItem>(collectionView: self) {
(collectionView: UICollectionView, indexPath: IndexPath, identifier: ListItem) -> UICollectionViewCell? in
let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
cell.accessories = [.disclosureIndicator()]
return cell
}
}
}
我正在使用 List
这样的
class DeclarativeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
let collectionView = List {
ListItem("Cell 1")
ListItem("Cell 2")
ListItem("Cell 3")
ListItem("paperplane.fill")
ListItem("doc.text")
ListItem("book.fill")
}
collectionView.frame = view.bounds
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(collectionView)
}
}
这似乎有效。我根据数据计数获取单元格行,但单元格是空的,如下所示:
我需要更改什么才能使其正常工作?我的错误在哪里?我可以在 UICollectionView
中定义数据源吗?谢谢你的回答。
您忘记应用配置。
var content = cell.defaultContentConfiguration()
content.image = item.image
content.text = item.title
cell.contentConfiguration = content // this is important
由于 UIListContentConfiguration
是 struct
,当您执行 var content = cell.defaultContentConfiguration()
时,您只会得到它的副本。