【问题标题】:RxSwift and UICollectionView HeaderRxSwift 和 UICollectionView 标头
【发布时间】:2016-09-19 18:47:07
【问题描述】:

我将RxSwift 与包含UICollectionViewUIViewController 一起使用。我试图在我的集合视图中添加一个标题,但这从未被调用:

func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath)

【问题讨论】:

    标签: ios swift uicollectionview rx-swift


    【解决方案1】:

    经过很长时间弄清楚我有一个完整的答案。关键部分取决于 RxDataSources dataSource.configureCell dataSource.supplementaryViewFactory。

    您需要通过正常注册您的笔尖进行设置。要使集合视图显示它的标题,您需要定义标题大小。使用集合流布局允许您保留您可能在单元格内为点击处理程序提供的任何绑定,并且不会覆盖 RxCocoa 内部使用的 rx 委托代理。

    在这个例子中,我有一个用于整个屏幕的 viewModel,它包含一个 RxVariable,其中包含集合视图中每个项目的 itemViewModels 数组。

    带有使用 RxSwift、RxDataSources 和 RxCocoa 的标头的 UICollectionView:

    var disposeBag: DisposeBag? = DisposeBag()
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel<String, ItemViewModel>>()
     
    override func viewDidLoad() {
        super.viewDidLoad()
        registerHeaderCell()
        registerCollectionViewCell()
        setupLayout()
        bindViewModelToCollectionView()
    }
    
    private func registerHeaderCell() {
        let nib = UINib(nibName: "HeaderCell", bundle: nil)
        collectionView.register(nib, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderReuseId")
    }
    
    private func registerCollectionViewCell() {
        let nib = UINib(nibName:"CollectionViewCell", bundle: nil)
        collectionView.register(nib, forCellWithReuseIdentifier: "CellReuseId")
    }
    
    private func setupLayout(){
        let layout = UICollectionViewFlowLayout()
        layout.headerReferenceSize = CGSize(width: UIScreen.main.bounds.width, height: 100)
        collectionView.setCollectionViewLayout(layout, animated: false)
    }
    
    private func bindViewModelToCollectionView(){
        dataSource.supplementaryViewFactory = {(dataSource, collectionView, kind, indexPath) -> UICollectionReusableView in
            let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "HeaderReuseId", for: indexPath) as! HeaderCell
            header.setup()
            return header
        }
        
        dataSource.configureCell = {(datasource, collectionView, indexPath, itemViewModel) -> UICollectionViewCell in
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CellReuseId", for: indexPath) as! CollectionViewCell
            cell.setup()
            return cell
        } 
    
        viewModel.itemViewModels.asObservable().map { 
            (itemViewModels) -> [SectionModel<String, ItemViewModel>] in
           //my particular app has one big section but you can use this for mutiple sections like Sergiy's answer   
             return [SectionModel(model: "", items: itemViewModels)]
        }.bind(to: collectionView.rx.items(dataSource: dataSource))
        .addDisposableTo(disposeBag!)
    }
    
    deinit {
        disposeBag = nil
    }
    

    【讨论】:

    • 为了使这个答案更有意义,您的 setup() 函数至少应该将 indexPath 作为参数(因为您很可能希望根据 indexPath 配置单元格)。更好的是,只需在此处发表评论:// Set up cell
    • 没错,setup 函数在这里只是作为示例的占位符。我把它留在里面是因为它可能不是很明显这是一个调用这样一个函数的好地方。但是,是的,如果您希望不同的单元格以不同的方式显示,则需要在其中传递 indexPath。您还可以使用类型擦除来加载不同类型的单元格来做一些棘手的事情。为简洁起见,省略了很多内容。更多示例在 RxDatasources github 中,但是,我在此处添加了此示例,因为我很难找到专门针对标头的谨慎示例。
    【解决方案2】:

    我有同样的问题。解决它移到RxCollectionViewSectionedReloadDataSource

    这种类型的数据源有页眉/页脚。标准的RxCollectionViewReactiveArrayDataSource 没有页眉/页脚。

    即使在 RxCocoa UICollectionView+Rx 扩展中也提供了 RxCollectionViewSectionedReloadDataSource 作为示例。

    例子:

         let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel<String, Double>>()
    
         let items = Observable.just([
             SectionModel(model: "First section", items: [
                 1.0,
                 2.0,
                 3.0
             ]),
             SectionModel(model: "Second section", items: [
                 1.0,
                 2.0,
                 3.0
             ]),
             SectionModel(model: "Third section", items: [
                 1.0,
                 2.0,
                 3.0
             ])
         ])
    
         dataSource.configureCell = { (dataSource, cv, indexPath, element) in
             let cell = cv.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! NumberCell
             cell.value?.text = "\(element) @ row \(indexPath.row)"
             return cell
         }
    
         items
            .bind(to: collectionView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
    

    【讨论】:

    【解决方案3】:

    它与RxSwift 无关,除非你使用的是RxDataSources,也就是说。

    您只是没有正确设置您的UICollectionView。除其他事项外:

    • 确保定义 func numberOfSections(in collectionView: UICollectionView) -&gt; Int 以返回大于 0 的值。
    • 确保定义 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -&gt; CGSize 以返回大于 CGSize(width: 0, height: 0) 的大小。
    • 如果您使用的是nib,请向func register(_ nib: UINib?, forSupplementaryViewOfKind kind: String, withReuseIdentifier identifier: String) 注册。或者,如果您以编程方式创建它,则使用 func register(_ viewClass: AnyClass?, forSupplementaryViewOfKind elementKind: String, withReuseIdentifier identifier: String) 注册它。

    【讨论】:

    • 我已经尝试过您的解决方案,但没有帮助。我的假设是 func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) 是在 UICollectionViewDataSource 协议中定义的,它是由 RxSwift 实现的。还能是什么?也许我应该用更多信息更新我的问题?
    • 我认为添加更多信息不会有帮助,因为您基本上必须交出所有代码。我建议您查看UICollectionView 教程并严格按照它来确保您得到一些作为基础的东西。然后从那里修改它。
    猜你喜欢
    • 1970-01-01
    • 2018-01-30
    • 2012-09-18
    • 2021-06-11
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    相关资源
    最近更新 更多