13

In my UITableViewCell I have a button. And I want to add action to it by passing multiple parameters in cellForRowAtIndexPath method.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("CartCell", forIndexPath:
        indexPath) as! CartTableViewCell
    cell.buyButton.addTarget(self, action: self.buyButton(indexPath, 2, 3 ,4 , 5, 6), forControlEvents: .TouchUpInside)
}
owacoder
  • 4,815
  • 20
  • 47
Arti
  • 7,356
  • 12
  • 57
  • 122
  • 1
    No, you can't do this. The action isn't a closure, it is a selector and the selector must take 0 or 1 arguments, being the sender button. Rather than adding an action to the button in `cellForRowAtIndexPath` you should define a protocol and set your view controller as the cells delegate. – Paulw11 Nov 03 '15 at 11:56
  • can you show example, please – Arti Nov 03 '15 at 12:31
  • You can use the solution provided in this answer regarding UIButton and passing mulitple parameters to its selector: https://stackoverflow.com/a/53779104/5324541 – Lloyd Keijzer Dec 14 '18 at 11:55

5 Answers5

44

May be you can do something like this

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

    let cell = tableView.dequeueReusableCellWithIdentifier("CartCell", forIndexPath:indexPath) as! CartTableViewCell
    cell.buyButton.tag = (indexPath.section*100)+indexPath.row
    cell.buyButton.addTarget(self, action: "btnBuy_Click:", forControlEvents: .TouchUpInside)
}

func btnBuy_Click(sender: UIButton) {
        //Perform actions here
     let section = sender.tag / 100
     let row = sender.tag % 100
     let indexPath = NSIndexPath(forRow: row, inSection: section)
     self.buyButton(indexPath, 2, 3 ,4 , 5, 6)
}

Create tag value according to you'r requirement and maintaint it's integrity too.

Ankita Shah
  • 2,178
  • 3
  • 25
  • 42
  • 1
    This is a ridiculous hack. It works but it is super hacky – Michael Jul 27 '16 at 20:00
  • Superb idea and thanks :), But totally wrong when i have 100 sections and 100 rows then, if i select 100th row on 100th section then selected section becomes ZERO and row becomes 100. – Rajamohan S Jan 07 '17 at 15:20
  • Well, maybe you can use 1000 instead of 100. I don't think anyone wants to displays 1000 rows. – Maziyar Mar 09 '18 at 13:13
  • what if we have multiple parameters in the function. How would the #selector look like. func btnBuy_Click(sender: UIButton, title: String). – Taimur Ajmal Sep 24 '19 at 12:00
13

If you need to pass string just use accessibilityHint of UIButton.

Tag can only store Int so if anyone wants to pass string data can use this. However, this not a proper way of passing data!

example:

button.accessibilityHint = "your string data"
button.addTarget(self, action: Selector(("buttonTapped")), for: UIControlEvents.touchUpInside)

func buttonTapped (sender: UIButton) {
   let section = sender.accessibilityHint
}
Yalcin Ozdemir
  • 524
  • 3
  • 7
  • Creative hack, made me smirk. Thanks – SQLiteNoob Nov 08 '17 at 19:32
  • No, don't use the accessibility hint. That's used by blind users to figure out which views are which is VoiceOver. You **can** use the accessibility identifier however. That's only used by your app. (Some test frameworks like KIF can use the accessibility identifier to find fields for testing purposes, but that's an unusual case. – Duncan C Dec 08 '17 at 03:52
9

Easy!

1: Subclass UIButton:

class IndexPathCellButton: UIButton {
    var indexPath:NSIndexPath?
}

2: Set your value:

...
cell.indexPathButton.addTarget(self, action: #selector(LocationSearchViewController.cellOptionButtonClick(_:)), forControlEvents: .TouchUpInside)
cell.indexPathButton.indexPath = indexPath // Your value
...

3: Retrieve your value:

@objc private func cellOptionButtonClick(sender: IndexPathCellButton) {
    print(sender.indexPath)
}
Michael
  • 9,639
  • 3
  • 64
  • 69
9

I think we should architect the code well. The hacks will just make your code hard to understand and buggy. Here is some suggestion.

Make a custom button class

class CustomButton:UIButton {
var name:String = ""
var object:MyObjectClassType?

convenience init(name: String, object: MyObjectClassType) {
   self.init(frame:CGRectZero)
   self.name =name
   self.object = object!
}}

Now add target to your button

let btn = your custom button
btn.name = “Your name”
btn.object = any object
btn.addTarget(self, action: #selector(myMethod(_:)), forControlEvents:.TouchUpInside)

Now simply get your values in the method call

  @IBAction func myMethod(sender:CustomButton) {
    print(sender.name)
}
sheetal
  • 3,014
  • 2
  • 31
  • 45
  • It's a neat solution, think you can achieve that via IB ? – OhadM Jul 26 '17 at 14:26
  • @OhadM it will be the same flow.. except the the connected UIButton class type in storyboard can be changed to CustomButton.. The init() / init?(coder aDecoder: NSCoder) and awakeFromNib() methods will get called on CustomButton class before loading but you can always set variables when you have the values ready.. Like in this case before table view delegate calls – sheetal Jul 26 '17 at 18:17
  • Sounds like s good idea but I am still missing few parts, if we will create a custom button as you said, we will have to set the name and object properties before the actual tap ? What about if the button is inside a cell and I am using responder-chain to receive the action inside the VC, how can we achieve that using your idea ? How should we set the properties in this use-case ? Any chance you could edit the answer and add a code snip or example ? Thanks :) – OhadM Jul 27 '17 at 07:30
3

You can use the tag property on the UIButtons's titleLabel and UIImageView to add additional parameters

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
{
let cell = tableView.dequeueReusableCellWithIdentifier("CartCell", forIndexPath:indexPath) as! CartTableViewCell
cell.buyButton.tag = (indexPath.section*100)+indexPath.row
cell.buyButton.imageView.tag = 2
cell.buyButton.titleLabel.tag = 3
cell.buyButton.addTarget(self, action: "btnBuy_Click:", forControlEvents: .TouchUpInside)
}
DeyaEldeen
  • 10,847
  • 10
  • 42
  • 75