Marko Cain
Marko Cain

Reputation: 15

How to get selected table view cell row in segue?

I am developing an iOS app using Swift and I have a view controller that segues from a table view cell content view by selecting a cell row or selecting a button inside of that cell row's content view. The original view controller that contains the table view performs a segue on two different occasions: one segue when the cell row itself is selected (segues to an avplayerviewcontroller and plays a video depending on the cell row that was selected) and the second segue happens when you press a button that is inside of the content view of the table view cell. In the first segue, I am able to pass the the cell row that is selected with if let indexPath = self.tableview.indexPathForSelectedRow when I override the first segue. However when I try to pass the cell row that was selected when I try to override the second segue that happens when you press the button it doesn't work. Is this because the button inside of the table view cell doesn't know which row it was selected in? If so, how can I solve this problem, and if not what is a viable solution to solve such issue? Reminder: The "playDrill" segue is trigged when you select a cell row, the "Tips" segue is trigged when you selected a button inside of that same cell row's content view

Code for first segue that happens when you select a cell row (this segue functions as desired):

class DrillsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "playDrill" {
        if let indexPath = self.tableView.indexPathForSelectedRow {
            if initialRow == 1 {
                drillVid = videoURL[indexPath.row]
                playerViewController = segue.destination as! PlayerController
                playerViewController.player = AVPlayer(playerItem: AVPlayerItem(url: drillVid))
                playerViewController.player?.play()
                print(indexPath) //prints correct value which is "[0,6]"
            }
            if initialRow == 3 {
                drillVid = videoUrl[indexPath.row]
                playerViewController = segue.destination as! PlayerController
                playerViewController.player = AVPlayer(playerItem: AVPlayerItem(url: drillVid))
                playerViewController.player?.play()
            }

Code for second segue that triggers when you select a button inside of the cell's content view (I want this segue to have the value of indexPath as in the first segue, but when I try to use that code it doesn't return the correct value):

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "Tips" {
       if let indexPath = self.tableview.indexPathForSelectedRow {
        if initialRow == 1 {
            print(indexPath)  //the value printed is "6", I want "[0,6]" like the first code block
            let tipVC = segue.destination as! KeysController

            } 
        }
    }
}

Upvotes: 0

Views: 2372

Answers (3)

Shades
Shades

Reputation: 5616

You are not able to get the selected index of the selected cell because you are not actually selecting a cell. You are pressing a button inside the cell.

So, what you do is get a reference to the button, get the button's superview (the cell) and then you can get the indexPath of that cell.

class TableViewController: UITableViewController {

    var indexPathForButton: IndexPath?

    @IBAction func buttonPressed(_ sender: UIButton) {

        let button = sender
        let cell = button.superview!.superview! as! MyCell  // The number of levels deep for superviews depends on whether the button is directly inside the cell or in a view in the cell
        indexPathForButton = tableView.indexPath(for: cell)
    }

Then in prepare(for segue:)

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "Tips" {
        if initialRow == 1 {
          print(indexPathForButton)
          let tipVC = segue.destination as! KeysController
        }
    }
 }

Upvotes: 0

AlexWoe
AlexWoe

Reputation: 854

I had this issue also a couple of months ago... finally I was able to solve it with the following tasks:

  1. Create an Outlet and Action of the button in your corresponding TableViewCell

  2. Create a XYTableViewCellDelegate protocol in your TableViewCell.swift file

  3. Define a delegate of your previous created TableViewCell delegate in your TableViewCell class

  4. Define your delegate in cellForRowAt: function of your tableview

  5. Add the delegate to your ViewController class where the TableView is also implemented

  6. Finally just create this function in your ViewController to receive the tapped buttons tableview indexpath

If you need more help on this, please just copy / paste your whole ViewController & TableViewCell class here - We can make then the changes directly in the code.


It should look like the following code:

// FILE VIEWCONTORLLER
// -> 5.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, XYTableViewCellDelegate {

  // ... view did load stuff here

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell:TripDetailsTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell

      // -> 4.
      cell.delegate = self

    // ... 
  }

  // -> 6.
  func buttonTappedViewCellResponse(cell: UITableViewCell) {
     let indexPath = tableView.indexPath(for: cell)
     // do further stuff here
  }
}


// FILE TABLEVIEWCELL
// -> 2.
protocol XYTableViewCellDelegate {
    func buttonTappedViewCellResponse(cell:UITableViewCell)
}
class XYTableViewCell: UITableViewCell {
  // -> 1.
  @IBOutlet weak var button: UIButton!

  // -> 3.
  var delegate: XYTableViewCellDelegate?

  // -> 1.
  @IBAction func buttonAction(_ sender: Any) {
    self.delegate?.buttonTappedViewCellResponse(cell: self)
  }
}

Upvotes: 1

Julien Perrenoud
Julien Perrenoud

Reputation: 1589

I have never used tableview.indexPathForSelectedRow (and while I think it is not good practice, I am not sure if it is responsible for your issue), however one other approach you might try is to send the indexPath object as sender. This would avoid having to check for tableview.indexPathForSelectedRow inside prepareForSegue. For instance,

  • You can trigger your "playDrill" segue in tableView(UITableView, didSelectRowAt: IndexPath) where you have access to this indexPath and can simply pass it as sender.
  • When triggering your "Tips" segue, you can also pass this indexPath object as sender. One way to have a reference is to keep the indexPath object when you dequeue your cell and set the button action in ableView(UITableView, willDisplay: UITableViewCell, forRowAt: IndexPath)

Let me know if that helps you! Cheers,

Julien

Upvotes: 0

Related Questions