如何通过RxSwift的Observable为CollectionView的sizeForItemAt传递高度?
解决RxSwift下CollectionView动态高度获取问题
你的核心需求是在sizeForItemAt代理方法中,拿到Observable<[MovieSection]>里存储的单元格高度数据,这里有两种简洁可行的实现方式:
方案一:通过ViewModel直接获取最新数据
利用BehaviorRelay可以同步获取当前值的特性,在ViewModel中封装一个获取最新sections的属性,直接在代理方法中调用:
1. 修改ViewModel,添加同步获取数据的属性
class ViewModel { private let _sections = BehaviorRelay<[MovieSection]>(value: []) // 保持原有的Observable用于绑定 var sections: Observable<[MovieSection]> { return _sections.asObservable() } // 新增:对外暴露当前最新的sections数组 var currentSections: [MovieSection] { return _sections.value } func getData() { // 原逻辑不变 } }
2. 在代理方法中获取高度
直接通过ViewModel的currentSections拿到对应位置的枚举值,提取高度:
extension HomeViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { // 做边界判断,防止数组越界 guard indexPath.row < viewModel.currentSections.count else { return CGSize(width: collectionView.frame.width, height: 230) } let sectionItem = viewModel.currentSections[indexPath.row] switch sectionItem { case .header(let height, _): return CGSize(width: collectionView.frame.width, height: height) case .horizontal(_, let height, _): return CGSize(width: collectionView.frame.width, height: height) } } }
方案二:在ViewController本地缓存最新数据
如果不想修改ViewModel的结构,可以在ViewController中订阅sections,将最新数据缓存到本地变量,再在代理方法中使用:
1. 在ViewController中添加缓存变量
class HomeViewController: UIViewController { var viewModel = ViewModel() @IBOutlet weak var collectionView: UICollectionView! private let bag = DisposeBag() // 新增:缓存最新的sections数据 private var currentSections: [MovieSection] = [] // 原viewDidLoad等方法不变 }
2. 订阅sections更新缓存
在setupBindings中添加订阅逻辑:
private func setupBindings() { // 原有的items绑定逻辑不变 viewModel.sections .bind(to: collectionView.rx.items) { collectionView, row, item in // 原cell配置逻辑不变 } .disposed(by: bag) // 新增:订阅sections更新本地缓存 viewModel.sections .subscribe(onNext: { [weak self] sections in self?.currentSections = sections }) .disposed(by: bag) collectionView.rx.setDelegate(self) .disposed(by: bag) }
3. 在代理方法中使用缓存数据
extension HomeViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { guard indexPath.row < currentSections.count else { return CGSize(width: collectionView.frame.width, height: 230) } let sectionItem = currentSections[indexPath.row] switch sectionItem { case .header(let height, _): return CGSize(width: collectionView.frame.width, height: height) case .horizontal(_, let height, _): return CGSize(width: collectionView.frame.width, height: height) } } }
注意事项
- 两种方案都依赖
BehaviorRelay的特性:它的value属性是线程安全的,且会始终返回最新的订阅数据。 - 当ViewModel调用
_sections.accept()更新数据时,RxSwift会自动刷新CollectionView,此时sizeForItemAt会被重新调用,拿到最新的高度值。 - 必须添加边界判断(
indexPath.row < 数组长度),避免数组越界崩溃。
内容的提问来源于stack exchange,提问作者Popala Kyu
相关产品推荐
相关产品推荐

