I have a table view with RxDataSources on which cell items got a remove icon. when cells get dequeued and click on that remove icon, all the previous click events get triggered, thus duplicate tap. Item cell :
removeImageView.rx.tap().map { _ in indexPath }
.bind(to: viewModel.onRemoveItem).disposed(by: cellDisposeBag)
Cell viewmodel:
let onRemoveItem = PublishSubject<IndexPath>()
View controller view model where the cell and ViewModel get bound:
let vm = ItemViewModel(with: item)
vm.onRemoveItem.bind(to: self.onRemoveItem).disposed(by: self.rx.disposeBag)
return SectionItem.item(viewModel: vm)
View Controller:
let dataSource = RxTableViewSectionedReloadDataSource<SectionItem>(configureCell: { dataSource, tableView, indexPath, item in
switch item {
case .item(let viewModel):
let cell = (tableView.dequeueReusableCell(withIdentifier: itemtIdentifier, for: indexPath) as? ItemCell)!
cell.bind(to: viewModel, at: indexPath)
return cell
}
}, titleForHeaderInSection: { dataSource, index in
let section = dataSource[index]
return section.title
} )
output?.items
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: rx.disposeBag)
output?.onRemoveCartIemTapped.distinctUntilChanged()
.skip(1)
.distinctUntilChanged().drive(onNext: { [weak self] (indexPath) in
print("onRemoveCartIemTapped" + String(indexPath.item))
}).disposed(by: rx.disposeBag)
Console debug:
onRemoveCartIemTapped0
onRemoveCartIemTapped3
onRemoveCartIemTapped1
onRemoveCartIemTapped4
This is caused by the UITableView
reusing the cell. To avoid having multiple subscriptions, you can override the cell's prepareForReuse()
method and ensure any existing subscriptions are disposed.
I usually declare the DisposeBag
as a var and then assign a new DisposeBag
to it in prepareForReuse()
. When the DisposeBag
is deinited it will dispose all of the subscriptions it contains. Something like:
override func prepareForReuse() {
super.prepareForReuse()
cellDisposeBag = DisposeBag()
}