Reputation: 49
I have a UITableView with custom UITableViewCell. Each cell has a button in it and I would like to make all the buttons unclickable when one button is clicked. It is easy to access the cell that contains the clicked button but I need also to access all the other cells to make their button unclickable.
So when a button is clicked inside a cell, I need to loop through all the other cells... I found a way to walk up the hierarchy so that in the button action method, I go up one layer above to access the UITableView and from there I can access each cell and from each cell I can access their button and edit the isUserInteractionEnabled property. However this doesn't seem to be a good practice to make a cell access the TableView and then all the other cells.
class AnswerCell2: UITableViewCell {
let answerTextButton: UIButton = {
let answerButton = UIButton()
answerButton.setTitle("initial text", for: .normal)
return answerButton
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String!) {
answerTextButton.addTarget(self, action: #selector(answerClicked), for: .touchUpInside)
}
@objc func answerClicked(sender: UIButton) {
// HERE I CAN ACCESS EASILY THE CELL WHERE THE BUTTON WAS CLICKED
// AND THEN USE THE TABLEVIEW TO ACCESS OTHER CELLS AND THEIR BUTTONS
// BUT THIS IS NOT A NICE WAY
}
So I have a solution as I said but I'd like more a nice way to do it. All the solutions I find online are about how to access the cell where the button was clicked but this one is easy, what I need is to access the other cells and I would like to avoid doing so in the answerClicked() action method if possible so that the code stays clean.
Thanks.
Upvotes: 1
Views: 907
Reputation: 4210
This could be achieved in a relatively light-weight approach thorugh either closures or delegate functions. Given tableView is already delegate-heavy I'd slightly prefer that approach.
create a simple protocol to allow the cell to inform the viewController
protocol CellStateRespondable { //I'm sure there are better protocol names than this!
var buttonsAreEnabled: Bool
func cellButtonChangedSate(enabled: Bool)
}
Add a delegate variable to your AnswerCell2 class
weak var delegate: CellStateRespondable?
and adapt youranswerClicked(sender: UIButton)
to call the delegate method
delegate?.cellButtonChangedState(enabled: sender.isEnabled)
make the ViewController (or other controller if you separate out tableView delegate functions) comply with the protocol through adding the variable and function, and add the function implementation
var buttonsAreEnabled = true
func cellButtonChangedSate(enabled: Bool) {
buttonsAreEnabled = enabled
if let visibleCells = tableView.visibleCells as? [AnswerCell2] {
visibleCells.forEach {
$0.button.isEnabled = enabled
}
}
}
and then adapt your cellForRowAt
to set the delegate and the state of the button when creating the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = //dequeue cell and configure as normal
cell.delegate. = self
cell.button.isEnabled = buttonsAreEnabled
return cell
}
Upvotes: 0
Reputation: 24341
You can create a closure
in custom UITableViewCell
and call it whenever a button
is pressed in the cell
, i.e.
class TableViewCell: UITableViewCell {
@IBOutlet weak var button: UIButton!
var handler: (()->())?
@IBAction func buttonClicked(_ sender: UIButton) {
handler?()
}
}
Now, set the closure
in tableView(_:cellForRowAt:)
method and use visibleCells
property to enable/disable the buttons
in other cells
, i.e.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.textLabel?.text = arr[indexPath.row]
cell.handler = {
if let visibleCells = tableView.visibleCells as? [TableViewCell] {
visibleCells.forEach({
$0.button.isEnabled = ($0 === cell)
})
}
}
return cell
}
In case you want to persist the enable/disable button
states while reloading, you need to store in your model
.
Upvotes: 1
Reputation: 19758
I am not sure why you need to walk the hierarchy and specifically change the characteristics of the existing cells on screen.
Just store a value which represents the index of the selected button.
var indexPathForSelectedButton: IndexPath?
Anytime this changes, update the index in func answerClicked(sender: UIButton)
and reload the table view.
in the cell for at method, check if the current indexPath is the same as the stored one and if so enable the button, disable if not
Upvotes: 0