UICollectionView切换支付方式时重配置崩溃及自定义ContentConfiguration问题
解决UICollectionViewDiffableDataSource切换不同Cell类型时的崩溃与更新问题
问题本质
崩溃的核心原因是:调用snapshot.reconfigureItems()时,系统会复用现有Cell并重新配置,不允许在此过程中切换不同的Cell注册/复用标识符。你的代码在Cell Provider中根据paymentMethod返回不同类型的Cell,触发reconfigure时会出现「原Cell类型与新请求Cell类型不匹配」的冲突,直接导致崩溃。
方案一:通过更新Snapshot替换Item(推荐)
既然需要切换不同Cell类型,不能用reconfigureItems,而是要移除原Item再重新添加,让DiffableDataSource重新创建对应类型的Cell,而非复用旧Cell。
修改代码
- 更新
paymentMethod的didSet:
var paymentMethod: String = " Pay" { didSet { var snapshot = dataSource.snapshot() // 移除旧Item后重新添加,触发数据源创建新Cell snapshot.deleteItems(snapshot.itemIdentifiers) snapshot.appendItems(["demo"]) dataSource.apply(snapshot, animatingDifferences: true) } }
- 保持Cell Provider的
weak self捕获,确保能拿到最新的paymentMethod:
dataSource = .init(collectionView: collectionView) { [weak self] collectionView, indexPath, itemIdentifier in guard let self else { return nil } return switch self.paymentMethod { case " Pay": collectionView.dequeueConfiguredReusableCell(using: applePayButtonCellRegistration, for: indexPath, item: itemIdentifier) default: collectionView.dequeueConfiguredReusableCell(using: payCashAtDeliveryCellRegistration, for: indexPath, item: itemIdentifier) } }
此方案既保证了weak self的内存安全,又能让数据源根据最新的支付方式创建正确的Cell,不会触发崩溃。
方案二:使用单个Cell类型,内部切换视图
如果需要平滑的更新动画,可采用通用Cell,内部根据支付方式显示/隐藏对应按钮,这样就能正常使用reconfigureItems更新内容。
修改代码
- 创建通用Cell:
class PaymentButtonCell: UICollectionViewListCell { let applePayButton = PKPaymentButton(paymentButtonType: .checkout, paymentButtonStyle: .automatic) let cashButton: UIButton = { var config = UIButton.Configuration.plain() var attr = AttributeContainer() attr.font = .preferredFont(forTextStyle: .headline) attr.foregroundColor = .systemBlue config.attributedTitle = .init("Ordina", attributes: attr) return UIButton(configuration: config, primaryAction: nil) }() override init(frame: CGRect) { super.init(frame: frame) [applePayButton, cashButton].forEach { $0.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview($0) NSLayoutConstraint.activate([ $0.topAnchor.constraint(equalTo: contentView.topAnchor), $0.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), $0.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), $0.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), $0.heightAnchor.constraint(greaterThanOrEqualToConstant: 44) ]) } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(with paymentMethod: String, target: Any?, action: Selector) { applePayButton.isHidden = paymentMethod != " Pay" cashButton.isHidden = paymentMethod == " Pay" // 先移除旧Target避免重复绑定 applePayButton.removeTarget(target, action: action, for: .touchUpInside) cashButton.removeTarget(target, action: action, for: .touchUpInside) applePayButton.addTarget(target, action: action, for: .touchUpInside) cashButton.addTarget(target, action: action, for: .touchUpInside) } }
- 修改数据源配置:
func configureDataSource() { let cellRegistration = UICollectionView.CellRegistration<PaymentButtonCell, String> { [weak self] cell, indexPath, itemIdentifier in guard let self else { return } cell.configure(with: self.paymentMethod, target: self, action: #selector(self.checkout)) } 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) }
- 保持
paymentMethod的didSet逻辑不变:
var paymentMethod: String = " Pay" { didSet { var snapshot = dataSource.snapshot() snapshot.reconfigureItems(snapshot.itemIdentifiers) dataSource.apply(snapshot, animatingDifferences: true) } }
此方案避免了Cell类型切换,适合需要平滑过渡动画的场景,且不会触发性能警告。
之前尝试的问题解析
- 捕获
[paymentMethod]是值捕获,只会拿到configureDataSource执行时的初始值,后续paymentMethod更新时,闭包内仍为旧值,导致Cell不更新。 reconfigureItems的设计目的是同一Cell类型下的内容更新,切换Cell类型违反了DiffableDataSource的规则,必然触发崩溃。
内容的提问来源于stack exchange,提问作者Filippo
相关产品推荐
相关产品推荐

