设置UIBackgroundConfiguration的backgroundColor破坏默认颜色转换器问题
iOS 14+ UICollectionView列表单元格自定义背景色后丢失默认选中状态问题
在iOS 14及以上版本的UIKit中,我基于列表布局和Diffable DataSource创建了单分区单行的UICollectionView,核心实现代码如下:
class ViewController: UIViewController { var collectionView: UICollectionView! var dataSource: UICollectionViewDiffableDataSource<String, String>! override func viewDidLoad() { super.viewDidLoad() configureHierarchy() configureDataSource() } func configureHierarchy() { collectionView = .init(frame: .zero, collectionViewLayout: createLayout()) view.addSubview(collectionView) collectionView.frame = view.bounds collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] } func createLayout() -> UICollectionViewLayout { UICollectionViewCompositionalLayout { section, layoutEnvironment in let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) return NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment) } } func configureDataSource() { let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { cell, indexPath, itemIdentifier in var backgroundConfiguration = UIBackgroundConfiguration.listGroupedCell() backgroundConfiguration.backgroundColor = .systemBlue cell.backgroundConfiguration = backgroundConfiguration } dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier) } var snapshot = NSDiffableDataSourceSnapshot<String, String>() snapshot.appendSections(["main"]) snapshot.appendItems(["demo"]) dataSource.apply(snapshot, animatingDifferences: false) } }
设置UIBackgroundConfiguration的backgroundColor为.systemBlue后,点击单元格无法显示默认选中状态——直接设置颜色会破坏单元格默认的UIConfigurationColorTransformer。我的需求是:保持自定义背景色,同时让单元格选中表现与未设置该属性时完全一致,且必须采用iOS 14推荐的Configuration方式配置外观。
尝试过的无效方法
- 设置CollectionView代理允许选中行
- 将颜色转换器设为
.grayscale - 为
backgroundConfiguration应用updated(for:)方法 - 引用默认配置的颜色转换器
- 使用CollectionViewController并禁用自动清除选中
- 切换
automaticallyUpdatesBackgroundConfiguration属性 - 将配置代码放入
configurationUpdateHandler - 组合上述多种方法
- 引用列表单元格配置的颜色转换器(均为nil)
临时可行解决方案
目前有两种可实现需求的临时方案,但都不符合理想预期:
方案1:自定义颜色转换器
根据单元格状态返回对应颜色:
var backgroundConfiguration = UIBackgroundConfiguration.listGroupedCell() backgroundConfiguration.backgroundColorTransformer = .init { _ in if cell.configurationState.isSelected || cell.configurationState.isHighlighted || cell.configurationState.isFocused { .systemRed } else { .systemBlue } } cell.backgroundConfiguration = backgroundConfiguration
方案2:改用backgroundView和selectedBackgroundView
放弃使用BackgroundConfiguration,直接设置背景视图:
let v = UIView() v.backgroundColor = .systemBlue cell.selectedBackgroundView = v
或完整配置内容与背景视图:
var contentConfiguration = UIListContentConfiguration.cell() contentConfiguration.text = "Hello" cell.contentConfiguration = contentConfiguration let v = UIView() v.backgroundColor = .systemBlue cell.backgroundView = v let bv = UIView() bv.backgroundColor = .systemRed cell.selectedBackgroundView = bv
两种方案均兼容视图出现时取消选中的逻辑:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) deselectSelectedItems(animated: animated) } func deselectSelectedItems(animated: Bool) { if let indexPath = collectionView.indexPathsForSelectedItems?.first { if let coordinator = transitionCoordinator { coordinator.animate(alongsideTransition: { [weak self] context in self?.collectionView.deselectItem(at: indexPath, animated: true) }) { [weak self] (context) in if context.isCancelled { self?.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: []) } } } else { collectionView.deselectItem(at: indexPath, animated: animated) } } }
寻求理想解决方案
希望找到无需自定义颜色转换器、也无需放弃Configuration的方案——即通过恢复默认设置,在保留自定义背景色的同时,让单元格拥有原生的选中状态表现。
内容的提问来源于stack exchange,提问作者Filippo
相关产品推荐
相关产品推荐

