You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 12:41:06