RxSwift modify tableview cell on select
You can gain access to the cell like this
tableView.rx.itemSelected
.subscribe(onNext: { [weak self] indexPath in
let cell = self?.tableview.cellForRow(at: indexPath) as? SomeCellClass
cell.button.isEnabled = false
}).addDisposableTo(disposeBag)
//to get access to model
tableView.rx.modelSelected(Item.self)
.subscribe(onNext: { [weak self] model in
guard let self = self else { return }
self.selectedItem = model
}).disposed(by: disposeBag)
//to get access to indexPath
eg: var names = ["A", "B", "C"]
tableView.rx.itemSelected
.subscribe(onNext: { [weak self] indexPath in
guard let self = self else { return }
self.selectedName = self.names[indexPath.row]
self.performSegue(withIdentifier: "ItemDetail", sender: self)
}).disposed(by: disposeBag)
You can have both in the file if you want to get a handle of the model as well as the index path
I'm not a big fan of the accepted answer because in that answer, there is no model that keeps track of the block state. Instead it just disables the button. It's important to have the state of your Views follow the Models and the accepted answer ignores that.
The following is obviously more complex, but it sets up the model and makes the state of the view reflect the underlying model instead of avoiding the model.
enum Input {
case reset([ContactNameNumberBlockStatus])
case blockTapped(UUID)
}
struct State {
var order: [UUID] = []
var values: [UUID: ContactNameNumberBlockStatus] = [:]
}
let taps = PublishSubject<UUID>()
let state = Observable.merge(
contacts.map { Input.reset($0) },
taps.map { Input.blockTapped($0) }
)
.scan(into: State()) { (state, input) in
switch input {
case .reset(let contacts):
state.order = contacts.map { _ in UUID() }
state.values = Dictionary.init(zip(state.order, contacts), uniquingKeysWith: { lhs, _ in lhs })
case .blockTapped(let uuid):
state.values[uuid]!.blockStatus = true
}
}
state
.map { $0.order }
.bind(to: tableView.rx.items(cellIdentifier: "BlockListTableViewCell", cellType: BlockListTableViewCell.self)) { row, uuid, cell in
let cellState = state.compactMap { $0.values[uuid] }
cell.disposeBag.insert(
cellState.map { $0.contactThumbnail.flatMap { UIImage(data: $0) } }.bind(to: cell.contactImage.rx.image),
cellState.map { $0.contactName }.bind(to: cell.contactName.rx.text),
cellState.map { $0.contactNumber }.bind(to: cell.contactNumber.rx.text),
cellState.map { $0.blockStatus }.bind(to: cell.blockButton.rx.isHidden),
cell.blockButton.rx.tap.map { uuid }.bind(to: taps)
)
}
.disposed(by: disposeBag)
The above code sets up a state machine that keeps track of users and updates the user model as necessary, which in turn causes the view to update. It's much more extensible; when a new user input is discovered, just add a case to the Input
type. Also, when new state is discovered, that can just be added to the State type.
The code also has an added benefit; updating the state of an item in the model does not cause the entire table view to reload. Only that cell will be changed.