Search code examples
swiftbuttontableviewsegue

How to pass button's sender.tag to another VC when clicking a button which triggering open another VC


I'm using a custom tableView Cell, which has a button and some labels in VC1. What I want to do is when I click the button in a row, then open VC2 and send the current label's text(names[i]) to VC2.

But in my case, I stuck at how to pass buttonTag in pressButton func to prepare func controller.playingSong = names[buttonTag]. I try to use a global var, then each time prepare() to retrieve that var, but it didn't work property, it seems prepare() will executed before pressButton() method.

Well, I do search a lot but didn't get a solution, could I combine prepare() into my pressButton(), in other word, is the prepare is necessary when using the segue?

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var myTableView: UITableView!
    let REUSE_ID = "CustomCell"
    var names = ["first", "second", "third"]

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func pressButton(_ sender: UIButton) {
        let buttonTag = sender.tag
    }

}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return names.count
    }


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

        cell.myButton.tag = indexPath.row
        cell.songName.text = names[indexPath.row]
        // cell.myButton.addTarget(self, action: #selector(self.pressButton(_:)), for: .touchUpInside)

        return cell
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        switch segue.identifier {
            case "playingSongs":
                let controller = segue.destination as! OpenedVC
                controller.playingSong = names[buttonTag]
            default: break
        }
    }
}

Solution

  • It sounds like you are attaching a segue to your button. You can do that, or use a button IBAction. I don't think it works correctly if you do both, which may be the problem you are having.

    If you have your button press trigger an IBAction, you can fetch the button tag from the sender parameter to the action, and then have your IBAction code instantiate the view controller and set a property of the view controller to the sender tag before displaying it.

    If you want to attach a segue to the button, then the prepare(for:sender:) method will get passed the button in the sender parameter. You can then try to cast sender to `UIButton, and if the cast succeeds, fetch the button's tag and pass it along to the destination view controller. Something like this:

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        switch segue.identifier {
            case "playingSongs":
                if let sendingButton = sender as? UIButton,
                   let controller = segue.destination as? OpenedVC { //Don't use a force-cast
                     let buttonTag = sendingButton.tag
                     controller.playingSong = names[buttonTag]
                }
            // The rest of your code to follow
         }
    }
    

    EDIT:

    I've fixed the code above by changing the two casts in the if let optional binding to use as?.

    Note that you should avoid force-casting (as!) since it causes a crash if it fails.