20

I want to perform a segue from a button within a custom UITableViewCell, but I don't know how to access the cell's contents when I push that button. I realize that this could be accomplished through didSelectRowAtIndexPath, however I have that method performing another function, and I would like to use the button I have created within the table cell.

Here is my perform segue method I'm having trouble with:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
    if segue.identifier == "showComments"
    {
        let vc:CommentOnPostViewController = segue.destinationViewController as CommentOnPostViewController

        var buttonPosition:CGPoint = sender?.convertPoint(CGPointZero, toView: self.feed) as CGPoint!
        var path:NSIndexPath = self.feed.indexPathForRowAtPoint(buttonPosition) as NSIndexPath!

        var postToCommentOn:PFObject = feedList[path.row] as PFObject
        vc.post = postToCommentOn as PFObject
    }
}

I tag the button in the cell when displaying it, and give it a target action:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    // There is code that goes here for creating and displaying the cell.

    cell.commentButton.tag = indexPath.row
    cell.commentButton.addTarget(self, action: "addComment", forControlEvents: UIControlEvents.TouchUpInside)
}

Here is the action that is called when pressing the button:

func addComment()
{
    self.performSegueWithIdentifier("showComments", sender: self)
}

Any help is appreciated. Thanks!

user3353890
  • 1,844
  • 1
  • 16
  • 31
  • May be you shoud pass not self, but the button or cell as sender? – eagle.dan.1349 Nov 12 '14 at 06:23
  • 1
    use a delegate to accomplish something like this. The delegate will be defined on the CustomCell class and on the TableViewController set the delegate of each cell... On the UITableViewController define the Method of the delegate that will call you self. performSegueWithIdentifier ... Just out of curiosity what are you doing with "didSelectRowAtIndexPath"? – S.H. Nov 13 '14 at 14:55

3 Answers3

49

Create a Protocol with the Method, that will be called by the CustomCell's Delegate, defined on your TableViewController

//Pass any objects, params you need to use on the 
//segue call to send to the next controller.

protocol MyCustomCellDelegator {
    func callSegueFromCell(myData dataobject: AnyObject)
}

Now use the Protocol on your UITableViewController

class MyTableViewController : UITableViewController, MyCustomCellDelegator {


 //The usual Defined methods by UIViewController and UITableViewController 

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

  //Set the CustomCell new Delegate
  var cell = tableView.dequeueReusableCellWithIdentifier(customIdentifier) as MyCustomCell


  cell.delagete = self

  return cell

 }


 //MARK: - MyCustomCellDelegator Methods

 func callSegueFromCell(myData dataobject: AnyObject) {
   //try not to send self, just to avoid retain cycles(depends on how you handle the code on the next controller)
   self.performSegueWithIdentifier("showComments", sender:dataobject )

 }

}

Define the Delegate on your Custom Cell and the Call inside your New Button the Delegate Function.

class MyCustomCell : UITableViewCell {

      var delegate:MyCustomCellDelegator!
      @IBOutlet weak var myButton:UIButton



     @IBAction func buttonPressed(sender:AnyObject){
           var mydata = "Anydata you want to send to the next controller"
           if(self.delegate != nil){ //Just to be safe.
             self.delegate.callSegueFromCell(mydata)
           }
    }
}

Hopefully this can be clean and clear for you to understand and implement in your code.

S.H.
  • 2,833
  • 3
  • 26
  • 28
  • How do I create the segue using storyboard? where do I CTRL+drag from? – Christopher Francisco May 04 '15 at 19:10
  • @ChristopherFrancisco CTRL + Drag from the controller you want to move from to the destination controller and it should show you some options like: Show, push, modal something like that. And thats it. Oh And make sure to add an identifier to the newly created segue. – S.H. May 04 '15 at 22:39
  • How do you create a protocol? Obviously mimic the code shown, but in which ViewController? Or are protocols standalone? If standalone, do I create a whole new Swift file? – Dave G Aug 19 '15 at 04:27
  • Also, thank you for the code but can you explain what exactly is happening on button press? My assumption is that the myButton (which is nested inside the cell) is linked as touchupInside to the 'buttonPressed' function. Upon being pressed the variable myData is created and passed to the protocol. But the protocol isn't in the main ViewController, so can it do much? Could it run code to delete the cell or modify it? – Dave G Aug 19 '15 at 04:35
  • @DaveG yes the Protocol is a standalone file. I usually like to seperate them for consistency. Also yes since its an IBAction it makes the button act like Touchupinside. The difference is that the action of the button that calls the method(TouchUpInside) is chosen on the Storyboard when I create the Button action there. – S.H. Aug 19 '15 at 14:25
  • Thanks Steve. I was totally new to protocols so my understanding was nil. I just used this as explained and it worked great. – Dave G Aug 20 '15 at 07:03
  • @StevenHernandez Can the protocol work both ways? In other words, I just used your code to make my customCellVC to trigger a function in my mainVC. Could I use the same protocol now to have a mainVC action trigger a customCellVC function? If so, are there any special tricks to doing so? – Dave G Aug 20 '15 at 07:48
  • it can work the other way around too. But you would need another protocol and inverse the delegate location and Method location. Meaning the Delegate would go into MainVC class and the Method definition of the Protocol would go on the Cell. – S.H. Aug 20 '15 at 12:09
  • if(self.delegate != nil){self.delegate.callSegueFromCell(mydata)} never gets executed. Could anyone please help?? – Arjun Oct 04 '19 at 04:34
5

I found an swiftier answer for passing data with a button in a dynamic cell to a new VC.

  1. Setting up a Tableview, a custom cell (connect the cell with your created class and set a cell-identifier) and create a new VC and connect it via a segue (also set an identifier for the segue) to your current TableViewController

  2. setting up your cell with the outlet (cellButton)

    class CustomCell: UITableViewCell {
        @IBOutlet weak var cellButton: UIButton!
    
    
       override func awakeFromNib() {
           super.awakeFromNib()
       }
    
       override func setSelected(_ selected: Bool, animated: Bool) {
          super.setSelected(selected, animated: animated)
       }
    }
    
  3. CellForRowAt function in the TableViewController-Class

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
        let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomCell
    
        //...Do stuff for your cell
    
        cell.cellButton.tag = indexPath.row //or value whatever you want (must be Int)
        cell.cellButton.addTarget(self, action: #selector(TableViewController.buttonTapped(_:)), for: UIControlEvents.touchUpInside)
     }
    
  4. set up the button method that's called by the target of the button

    func buttonTapped(_ sender:UIButton!){
        self.performSegue(withIdentifier: "customSegueIdentifier", sender: sender)
    }
    
  5. prepare for segue function

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if(segue.identifier == "customSegueIdentifier") {
            if let destination = segue.destination as? YourNewViewController {
    
               if let button:UIButton = sender as! UIButton? {
                   print(button.tag) //optional
                   destination.valueViaSegue = button.tag
               }
            }
        }
    }
    

Hope this code will help you guys. keep coding ;)

Marco Weber
  • 829
  • 10
  • 19
  • 2
    Thank you! I appreciate your input and your effort in compiling this solution! Would you mind explaining a little bit about how it's swiftier? – user3353890 Dec 24 '16 at 03:25
  • My `@IBOutlet weak var cellButton: UIButton!` shows up as nil inside of Step 3. Any ideas? **update** until someone responds... [this](https://stackoverflow.com/a/53043358/3997521) worked perfectly. – petrosmm Nov 16 '19 at 16:53
1

Here's what I did

In your UITableViewCell class, added the following

protocol MyCustomCellDelegator {

    func callSegueFromCell(myData dataobject: AnyObject)

}
class MyUITableViewCell: UITableViewCell {

    var delegate:MyCustomCellDelegator!

    .......


    @IBAction func buttonPress (_ sender: Any){


        if(self.delegate != nil){

            self.delegate.callSegueFromCell(myData: )

        }

    }

}

And in the UITableViewController

class STVHomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, MyCustomCellDelegator {


    .....

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


        if let cell = tableView.dequeueReusableCell(withIdentifier: "MyIdentifier") as? MyIdentifierCell {

            // configure your cell
            cell.configureCell(myIdentifier:)

            // This line is important 
            cell.delegate = self

            return cell

        } else {

            return MyIdentifierCell()
        }

    }

    func callSegueFromCell(myData dataobject: AnyObject) {

        print(dataobject)
        self.performSegue(withIdentifier: "goToNextVC", sender: dataobject )


    }

}
Arjun
  • 1,477
  • 1
  • 13
  • 23