UICollectionView中UIHostingConfiguration致@State变量复用异常的解决方法
我通过以下方式使用UIHostingConfiguration将SwiftUI视图嵌入UICollectionView:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) cell.contentConfiguration = UIHostingConfiguration { CellView() }
但CellView及其子视图中的@State var会被初始化为随机值,完全破坏了SwiftUI中@State的正常工作机制。复现的现象是:每个cell的count应初始化为0,但递增任意cell的count后,其他新分配的cell会显示非零值。
推测原因是UICollectionView复用cell时,内存拷贝了其他cell的视图,导致@State var保留了之前cell的值。UIKit的UICollectionViewCell有prepareForReuse函数用于清理和重新初始化cell状态,但SwiftUI中没有类似机制。
需求:如何在UICollectionView出列cell时重新初始化视图中的@State var,且不在View.init这类调用频繁的时机执行?
补充:理想的解决方案无需在每个嵌入UIHostingConfiguration的SwiftUI视图中添加代码,避免人为遗漏。
方案1:自定义UICollectionViewCell,重写prepareForReuse重置配置
创建自定义UICollectionViewCell子类,在复用回调中清空旧配置,强制生成全新的SwiftUI视图实例:
class HostingCollectionViewCell: UICollectionViewCell { override func prepareForReuse() { super.prepareForReuse() contentConfiguration = nil } }
使用时注册该自定义cell:
collectionView.register(HostingCollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
每次cell被复用时,旧的UIHostingConfiguration会被清空,重新设置时会创建新的CellView,@State也会随之重新初始化。
方案2:给UIHostingConfiguration绑定唯一标识
利用SwiftUI的id修饰符,给每个cell的配置绑定唯一的indexPath,当cell复用、indexPath变化时,SwiftUI会判定为全新视图,自动重置内部状态:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) cell.contentConfiguration = UIHostingConfiguration { CellView() } .id(indexPath)
该方案无需自定义cell,仅需在配置时添加id,借助SwiftUI的标识机制触发视图重建。
方案3:封装通用配置工具(无侵入式)
为了避免手动处理每个视图,封装一个UICollectionViewCell的扩展方法,自动处理复用逻辑:
extension UICollectionViewCell { func setHostingContentConfiguration<Content: View>(for indexPath: IndexPath, @ViewBuilder content: () -> Content) { contentConfiguration = UIHostingConfiguration { content() } .id(indexPath) } }
使用时直接调用该扩展方法:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) cell.setHostingContentConfiguration(for: indexPath) { CellView() }
所有嵌入SwiftUI视图的cell通过该方法配置,自动完成@State重置,避免人为遗漏。
内容的提问来源于stack exchange,提问作者MattL

