0

I have a button and a label in a table view (I am using 8 rows )and for some reason when I click the first button I get indexPath nil error, but when I click the second button (2nd row) I get the first row label. When I click the 3rd row button, I get the second row label etc. Why are they misaligned. I want when I click the first row button to get the first row label etc. Please see the code below. Thank you !!

   @objc func btnAction(_ sender: AnyObject) {


    var position: CGPoint = sender.convert(.zero, to: self.table)

    print (position)
    let indexPath = self.table.indexPathForRow(at: position)
    print (indexPath?.row)
    let cell: UITableViewCell = table.cellForRow(at: indexPath!)! as
    UITableViewCell


      print (indexPath?.row)
    print (currentAnimalArray[(indexPath?.row)!].name)
    GlobalVariable.addedExercises.append(currentAnimalArray[(indexPath?.row)!].name)

}


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? TableCell else {return UITableViewCell() }

   // print(indexPath)

    cell.nameLbl.text=currentAnimalArray[indexPath.row].name
 // print("\(#function) --- section = \(indexPath.section), row = \(indexPath.row)")

   // print (currentAnimalArray[indexPath.row].name)

    cell.b.tag = indexPath.row

   // print (indexPath.row)
    cell.b.addTarget(self, action: #selector(SecondVC.btnAction(_:)), for: .touchUpInside)


    return cell



}
Bubu
  • 67
  • 1
  • 8

2 Answers2

2

Frame math is a worst-case scenario if you have no choice. Here you have a lot of choices.

For example why don't you use the tag you assigned to the button?

@objc func btnAction(_ sender: UIButton) {
    GlobalVariable.addedExercises.append(currentAnimalArray[sender.tag].name)
}

A swiftier and more efficient solution is a callback closure:

In TableCell add the button action and a callback property. The outlet is not needed. Disconnect the outlet and connect the button to the action in Interface Builder. When the button is tapped the callback is called.

class TableCell: UITableViewCell {

    // @IBOutlet var b : UIButton!
    @IBOutlet var nameLbl : UILabel!

    var callback : (()->())?

    @IBAction func btnAction(_ sender: UIButton) {
        callback?()
    }
}

Remove the button action in the controller.

In cellForRow assign a closure to the callback property

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // no guard, the code must not crash. If it does you made a design mistake
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! TableCell

    let animal = currentAnimalArray[indexPath.row]
    cell.nameLbl.text = animal.name

    cell.callback = {
         GlobalVariable.addedExercises.append(animal.name)
    }

    return cell
}

You see the index path is actually not needed at all. The animal object is captured in the closure.

vadian
  • 274,689
  • 30
  • 353
  • 361
0

You already pass indexPath.row with button tag. Use the tag as index simply

@objc func btnAction(_ sender: UIButton) { 
    GlobalVariable.addedExercises.append(currentAnimalArray[sender.tag].name)
} 
Faysal Ahmed
  • 7,501
  • 5
  • 28
  • 50