Reputation: 109
I have a custom tableviewHeaderFooterView
where I set up a target event for the button in the custom tableViewCell
class (checkButton
is the button and its background image changes to a checkmark when a user clicks on it).
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let userModel = Data.userModels[section]
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId") as! SectionHeader
cell.setup(model: userModel)
cell.checkButton.tag = section
cell.checkButton.addTarget(self, action: #selector(handleTap), for: .touchUpInside)
return cell.contentView
}
And in that function I want to create or remove items from an array depending on whether the user taps on a cell or not (i.e. if they tap on the button, then add something to the array, but if they tap on that button again, then remove that object from the array.)
@objc func handleTap(sender: UIButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected == true {
let model = ItemModel(itemName: item, price: price)
ItemModelFunctions.createItem(for: sender.tag, using: model)
}
if sender.isSelected == false {
ItemModelFunctions.removeFromUser(from: sender.tag)
}
print(sender.tag)
}
Here are the createItem
and removeFromUser
functions:
struct ItemModelFunctions {
static func createItem(for userIndex: Int, using itemModel: ItemModel) {
Data.userModels[userIndex].itemModels.append(itemModel)
}
static func removeFromUser(from userIndex: Int) {
Data.itemModels.remove(at: userIndex)
}
}
When I tap on the button twice to remove it from the array, I get an error saying Data.itemModels.remove(at: userIndex)
is out of range.
I know using a prototype cell for a tableViewHeaderFooterView
isn't exactly the correct way, but I've seen other programmers and YouTubers do this with success. Are my issues coming from using a prototype cell? Or am I removing the item from the array in the wrong way? Thank you all for your help!
Upvotes: 0
Views: 74
Reputation: 949
// Please maintain one more array i.e selectedIndexArray and follow below code.
var selectedIndexArray = [Integer]()
@IBAction func buttonTapped(_ sender: UIButton) {
let button = sender
if selectedIndexArray.contains(button.tag) {
button.tag --> Remove this tag from selectedIndexArray
let model = ItemModel(itemName: item, price: price)
ItemModelFunctions.createItem(for: sender.tag, using: model)
} else
{
selectedIndexArray.append(button.tag)
ItemModelFunctions.removeFromUser(from: sender.tag)
}
//reload tableview.
self.tableView.reloadData()
}
Upvotes: 2
Reputation: 1630
Thanh Vu's solution works. Another solution would be to add an IBAction to your ViewController:
@IBAction func buttonTapped(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
let cell = sender.superview?.superview as! SectionHeader
if let indexPath = mainTableView.indexPath(for: cell) {
// if sender.isSelected etc...
}
}
Upvotes: 1
Reputation: 1739
The checkButton.addTarget function will run each time your section header is reused. Then it will duplicate event for cell when reuse many times. I think you should not use this solution. Instead of that, I think you should write delegate way to solve your problem. Ex:
protocol SectionHeaderDelegate: class {
func checkButtonDidTap(section: SectionHeader)
}
class SectionHeader: UITableViewCell {
weak var delegate: SectionHeaderDelegate
@IBOutlet weak var checkButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
checkButton.addTarget(self, action: #selector(handleTap), for: .touchUpInside)
}
func handleTap() {
self.delegate.checkButtonDidTap(section: self)
}
}
and you set cell.delegate = self in viewForHeaderInSection
function. And implement protocol SectionHeaderDelegate.
Upvotes: 1