I am trying to run an action for a button being pressed within a table view cell. The following code is in my table view controller class.
The button has been described as "yes" in an outlet in my class of UITableViewCell called requestsCell.
I am using Parse to save data and would like to update an object when the button is pressed. My objectIds array works fine, the cell.yes.tag also prints the correct number to the logs, however, I cannot get that number into my "connected" function in order to run my query properly.
I need a way to get the indexPath.row of the cell to find the proper objectId.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as requestsCell // Configure the cell... cell.name.text = requested[indexPath.row] imageFiles[indexPath.row].getDataInBackgroundWithBlock{ (imageData: NSData!, error: NSError!) -> Void in if error == nil { let image = UIImage(data: imageData) cell.userImage.image = image }else{ println("not working") } } cell.yes.tag = indexPath.row cell.yes.targetForAction("connected", withSender: self) println(cell.yes.tag) return cell } func connected(sender: UIButton!) { var query = PFQuery(className:"Contacts") query.getObjectInBackgroundWithId(objectIDs[sender.tag]) { (gameScore: PFObject!, error: NSError!) -> Void in if error != nil { NSLog("%@", error) } else { gameScore["connected"] = "yes" gameScore.save() } } }
add an 'indexPath` property to the custom table cell. initialize it in cellForRowAtIndexPath. move the tap handler from the view controller to the cell implementation. use the delegation pattern to notify the view controller about the tap event, passing the index path.
The tableview delegate methods are defined to add the following features to the tableview. We can create the custom headers and footers for the sections in the tableview. We can specify the custom heights for rows, headers, and footers. Provide height estimates for the rows, headers, and footers.
Swift 4 & Swift 5:
You need to add target for that button.
myButton.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)
And of course you need to set tag of that button since you are using it.
myButton.tag = indexPath.row
You can achieve this by subclassing UITableViewCell. Use it in interface builder, drop a button on that cell, connect it via outlet and there you go.
To get the tag in the connected function:
@objc func connected(sender: UIButton){ let buttonTag = sender.tag }
The accepted answer using button.tag
as information carrier which button has actually been pressed is solid and widely accepted but rather limited since a tag can only hold Int
s.
You can make use of Swift's awesome closure-capabilities to have greater flexibility and cleaner code.
I recommend this article: How to properly do buttons in table view cells using Swift closures by Jure Zove.
Applied to your problem:
Declare a variable that can hold a closure in your tableview cell like
var buttonTappedAction : ((UITableViewCell) -> Void)?
Add an action when the button is pressed that only executes the closure. You did it programmatically with cell.yes.targetForAction("connected", withSender: self)
but I would prefer an @IBAction
outlet :-)
@IBAction func buttonTap(sender: AnyObject) { tapAction?(self) }
func connected(sender: UIButton!) { ... }
as a closure to cell.tapAction = {<closure content here...>}
. Please refer to the article for a more precise explanation and please don't forget to break reference cycles when capturing variables from the environment.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With