G. Drijfhout G. Drijfhout - 26 days ago 9
iOS Question

Button in custom cell, ViewController method

Here's my situation.

I've got the following storyboard:
Storyboard
So, I've got a ViewController with a container view in it and a tableViewController. This tableViewController is embedded in the ContainerView through control-dragging from the one to the other.

Now, I have the following classes:

A ViewController:
This is the class of the ViewController in the storyboard.

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
}

func printText(message: String, custom: SomeCustomType) {
print("Button in cell clicked and parameters passed successfully to VC")
//....
}


Then I've got a TableViewController to manage the tableViewController:
This is the custom class of the tableViewController in the Storyboard.

Class TableViewController: UITableViewController {

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "TableViewCell"

guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TableViewCell else {
fatalError("The dequeued cell is not an instance of TableViewCell.")
}

// Configure the cell...

return cell
}

//And other functions such as 'numberOfSections', 'numberOfRows'
}


And finally, I have a tableViewCell class to connect all the cell's outlets to, etc.:
This is the custom class of the custom tableViewCell that's in the tableViewController.

Class TableViewCell: UITableViewCell {

@IBAction func buttonClickedInCell(_ sender: Any) {
}

}


What I want it to do is when I press the button in the cell, I want the method in the VC to run. I have tried multiple things, but haven't come to a way to do this yet. Can someone please help me out?

Thank you!

Answer Source

You should use a delegate for this.

In the storyboard, click on the embed segue and give it an identifier.

enter image description here

Make ViewController conform to TableViewCellDelegate and pass the instance of ViewController in prepare(for:sender:).

Set the delegate of the cell to the passed ViewController instance in TableViewController's tableView(_:cellForRowAt:). When the button is pressed, the method inside the view controller will be invoked.

Delegate

protocol TableViewCellDelegate: class {
    func tableViewCellButtonPressed(_ sender: Any)
}

TableViewCell

class TableViewCell: UITableViewCell {
    weak var delegate: TableViewCellDelegate?

    @IBAction func buttonClickedInCell(_ sender: Any) {
        delegate?.tableViewCellButtonPressed(sender)
    }
}

TableViewController

class TableViewController: UITableViewController {
    weak var tableViewCellDelegate: TableViewCellDelegate?

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "TableViewCell"

        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TableViewCell else {
            fatalError("The dequeued cell is not an instance of TableViewCell.")
        }

        cell.delegate = tableViewCellDelegate

        return cell
    }
}

ViewController

class ViewController: UIViewController, TableViewCellDelegate {
    var tableVc: TableViewController!

    func tableViewCellButtonPressed(_ sender: Any) {
        //do whatever you want after pressing the button
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "embedTable" {
            tableVc = segue.destination as! TableViewController
            tableVc.tableViewCellDelegate = self
        }
    }
}