What would the proper MVVM architecture be for a UICollectionViewController

时间:2016-04-04 17:10:42

标签: ios swift mvvm rx-swift

I am making an app for iPad using RXSwift and MVVM.

I have a UIViewController with a UICollectionView and a ViewModel which acts as the dataSource and the delegate for the collectionView.

Part of the collection cells' functionality is that when a button is tapped to present a popover. Now with the newer popover functionality in iOS 9 (possibly earlier) you need to present the view normally within the view controller, and modify the popoverPresentationController.

Now, as far as i'm aware you are not able to present a UIViewController from a UICollectionViewCell. Makes sense.

But the only way i thought to do this would be to have a delegate that points to the ViewController.

Looking at the class diagram (attached), the viewModel would have to set the delegate upon cell dequeue. To do that the ViewModel would have to know what ViewController to set as the delegate which i'm fairly sure goes against the point of the viewModel. According to MVVM (for iOS) the view model should not know about the view controller. The view controller can know about the view model.

And so my question is what would be the best way to do this following MVVM? If it requires moving the dataSource/Delegate to a different class i'm all for it.

UML Diagram

1 个答案:

答案 0 :(得分: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的原因。您还应该在其disposeBag方法中重新创建单元格prepareForReuse

class RootCollectionViewCell: UICollectionViewCell {

    var disposeBagCell:DisposeBag = DisposeBag()

    ...

    override func prepareForReuse() {
        disposeBagCell = DisposeBag()
    }

}
相关问题