【问题标题】:What would the proper MVVM architecture be for a UICollectionViewControllerUICollectionViewController 的正确 MVVM 架构是什么
【发布时间】:2026-01-13 01:50:02
【问题描述】:

我正在使用 RXSwift 和 MVVM 为 iPad 制作应用程序。

我有一个带有 UICollectionView 和 ViewModel 的 UIViewController,它充当数据源和 collectionView 的委托。

收集单元格的部分功能是当点击按钮以显示弹出框时。现在有了 iOS 9 中更新的弹出框功能(可能更早),您需要在视图控制器中正常显示视图,并修改 popoverPresentationController。

现在,据我所知,您无法从 UICollectionViewCell 呈现 UIViewController。有道理。

但我认为这样做的唯一方法是拥有一个指向 ViewController 的委托。

查看类图(附加),viewModel 必须在单元格出列时设置委托。要做到这一点,ViewModel 必须知道要设置什么 ViewController 作为委托,我相当肯定这与 viewModel 的观点背道而驰。根据 MVVM(对于 iOS),视图模型不应该知道视图控制器。视图控制器可以知道视图模型。

所以我的问题是在 MVVM 之后执行此操作的最佳方法是什么?如果它需要将 dataSource/Delegate 移动到不同的类,我完全赞成。

【问题讨论】:

    标签: ios swift mvvm rx-swift


    【解决方案1】:

    我认为视图模型根本不应该知道被点击的按钮。处理触摸事件属于视图层,以及呈现弹出框。

    这也表明您的视图模型不应该是UICollectionViewDataSource。所以加上RootCollectionViewCell,就是一个视图。不幸的是,这种耦合很难避免,因为 Apple 以这种方式设计了UICollectionViewDataSource。您可以提取一个单独的类作为数据源,也可以将数据源方法留在视图控制器中(在 iOS 上属于 MVVM 中的视图层)。

    使用 RxCocoa,您甚至可以完全避免实现 UICollectionViewDataSource 方法。看看UICollectionView+Rx 扩展。还有一个example in RxSwift repository(包含集合视图的表格视图单元格)。

    为了将按钮点击传递给视图控制器,您可以使用rx_tap Observable 并将其暴露在单元格的界面中。然后您可以在视图控制器(或单独的数据源类)中订阅生成的Observable

    //in the cell class
    var buttonTapped : ControlEvent<Void> {
        return button.rx_tap
    }
    
    //in the data source 
    cell.buttonTapped.subscribeNext {
      //show the popover
    }.addDisposableTo(cell.disposeBag)
    

    this answer 中所述,您应该避免在重复使用单元格时多次订阅同一个 Observable。这就是上面代码中使用cell.disposeBag 的原因。您还应该在其prepareForReuse 方法中重新创建单元格的disposeBag

    class RootCollectionViewCell: UICollectionViewCell {
    
        var disposeBagCell:DisposeBag = DisposeBag()
    
        ...
    
        override func prepareForReuse() {
            disposeBagCell = DisposeBag()
        }
    
    }
    

    【讨论】:

      最近更新 更多