问题
I'm using RxSwift
to bind a model array to a collection view
How do I get the model object from a given indexPath?
I'm doing the binding like this:
vm.bikeIssueCatagories()
.drive(self.collectionView.rx.items(cellIdentifier: "BikeIssueCategoryCollectionViewCell", cellType: UICollectionViewCell.self)) { row, data, cell in
}.disposed(by: disposeBag)
The core of my issue is, that I need to get both the model object and the cell that a user selects. Using collectionView.rx.modelSelected(T.self)
only gives me the model og type T
. And calling collectionView.rx.itemSelected
only gives me the selected IndexPath
collectionView.rx.itemSelected.asDriver()
.driveNext { [unowned self] indexPath in
guard let model = try? collectionView.rx.model(at: indexPath) else { return }
guard let cell = self.collectionView.cellForItem(at: indexPath) else { return }
}.disposed(by: disposeBag)
But this gives me an error when trying to the the model at indexPath:
Type 'inout UICollectionView' does not conform to protocol 'ReactiveCompatible'
Just trying:
let indexPath = IndexPath.init()
self.collectionView.rx.model(at: indexPath)
also gives me an error:
Ambiguous reference to member 'model(at:)'
SO... How to get both the model object and the cell that a user selects?
回答1:
I could have done like RamwiseMatt proposed. Making a method on my ViewModel that takes the IndexPath
and return the model. I did however end up using a zip
:
let modelSelected = collectionView.rx.modelSelected(SelectableBikeIssueCategory.self).asObservable()
let cellSelected = collectionView.rx.itemSelected.asObservable()
.map { [weak self] indexPath -> UICollectionViewCell in
guard let cell = self?.collectionView.cellForItem(at: indexPath) else { fatalError("Expected cell at indexpath") }
return cell
}
Observable.zip(cellSelected, modelSelected)
.map { [weak self] cell, category -> SomeThing in
return SomeThing()
}
回答2:
Your ViewModel defines a method bikeIssueCatagories()
, which is what your UIViewController binds the UICollectionView to. To get your model at the correct position, you can use the itemSelected
property you mentioned, which gives you an Observable<IndexPath>
, which you should feed into your ViewModel. There, you can use the IndexPath's item
property to determine which model from your data array (given in bikeIssueCatagories()
) is the one you are looking for. The modelSelected
property makes this easier for you since it already knows the datasource, so all you have to provide is the type of your Model (replace T.self
with YourModelName.self
.)
I'm not sure why you want a reference to your cell as well, but if you must, you can just use cellForItem(at:)
on your UICollectionView
(feeding it the IndexPath you obtained via itemSelected
.)
回答3:
Your self-accepted solution is not optimal, because the modelSelected stream maps indexPath internally and needs to be used when there is no need to know about indexPath. In your case, it is better to use use itemSelected and convert to a tuple for example.
collectionView.rx.itemSelected
.map { [weak self] indexPath in
guard
let model = try? self?.collectionView.rx.model(at: indexPath) as YourModelName?,
let cell = self?.collectionView.cellForItem(at: indexPath)
else {
preconditionFailure("Describing of error")
}
return (cell, model)
}
来源:https://stackoverflow.com/questions/46811630/get-model-from-uicollectionview-indexpath