6

I have this inside cellForRowAtIndexPath

   cell.plusBut.tag = indexPath.row
   cell.plusBut.addTarget(self, action: "plusHit:", forControlEvents: UIControlEvents.TouchUpInside)

and this function outside:

func plusHit(sender: UIButton!){
    buildings[sender.tag].something = somethingElse
}

Is it possible to send the indexPath.row and indexPath.section, or some alternative??

Thanks!

EDIT

I approached it like this:

My Custom Button

class MyButton: UIButton{

    var myRow: Int = 0
    var mySection: Int = 0

}

My Custom Cell

class NewsCell: UITableViewCell{

    @IBOutlet weak var greenLike: MyButton!

In CellForRowAtIndexPath

    cell.greenLike.myRow = indexPath.row

I get an error on this line.

Greg Peckory
  • 7,700
  • 21
  • 67
  • 114
  • There's a far better solution to this problem. In the button handler you use the button's (sender's) frame and ask the table view for the index path at the given location. Far better than trying to encode the row and section in the button somehow. – HangarRash Aug 25 '23 at 06:27

8 Answers8

20

Is it possible to send the indexPath.row and indexPath.section, or some alternative??

If you impose a limit on the number of possible rows in a section, you can combine the two into a single number. For example, if you're willing to say that there will always be fewer than 1000 rows in a given section, you can use:

cell.plusBut.tag = (indexPath.section * 1000) + indexPath.row

and then use mod and division operators to recover:

row = sender.tag % 1000
section = sender.tag / 1000

Another possibility is to check the button's superview (and it's superview, etc.) until you find the cell. Once you have the cell, you can get the index path for that cell from the table.

A third option, perhaps the best one, is to have the button target the cell rather than some other object. The cell can then trigger an action in the view controller or other object using itself as sender.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • What a nice solution... I was trying to do something along these lines as well. Obviously both section and row are assumed to be Integers – Greg Peckory Sep 02 '15 at 13:40
  • Excuse the question, but I don't understand. If section is 2 and I multiply by 1000 and add the row, 5 for example, I will have 2005. If I divide by 1000 I will obtain 2.005, that is 2. Correct. But the row? – Markus Nov 17 '20 at 14:31
  • @Markus If `sender.tag` is 2005, then `row = sender.tag % 1000`, and `2005 % 1000` is 5. `%` is the modulo operator, which gives the remainder after dividing it's first operand by it's second. `2005 / 1000 = 2, remainder 5`, so `2005 % 1000` is 5. – Caleb Nov 17 '20 at 15:16
  • @Markus Also note that since we're talking about integers, 2005 / 1000 = 2, not 2.005. – Caleb Nov 17 '20 at 19:16
  • Yes, yes, of course. I understood that 0.005 decimal were rounded to 0 because it is 0.005 and the variable is an integer. What I don't quite understand is the "% 1000" operation. – Markus Nov 19 '20 at 08:51
  • @Markus Modulo is a standard arithmetic operation that returns the remainder. See [Modulo operation](https://en.wikipedia.org/wiki/Modulo_operation) in Wikipedia. In this case I'm using `%` and `/` to store `row` and `section` together in a single number. That means that `row` has a to smaller than some limit (1000 in this case) and `section` has to be multiple of that limit. – Caleb Nov 19 '20 at 17:01
4

You can create a subclass of UIButton and create an extra property section in it. And then you can use that class for cell button. You can do the same for row.

Here are few possible ways after subclassing UIButton

cell.plusBut.tag = indexPath.row
cell.plusBut.section = indexPath.section

Or

cell.plusBut.row = indexPath.row
cell.plusBut.section = indexPath.section

Or

cell.plusBut.indexPath = indexPath

Choose whatever suits you.

Inder Kumar Rathore
  • 39,458
  • 17
  • 135
  • 184
3

Here's a really easy solution I use:

in CellForRow:

button.tag = indexPath.row
cell.contentView.superview?.tag = indexPath.section

in the function retrieve it like this:

func buttonTapped(_ sender: Any) {
    let button = sender as! UIButton
    let row = button.tag
    let sec = button.superview?.tag

    //retrieve item like self.array[sec][row]
    //do the rest of your stuff here
}
David Villegas
  • 458
  • 3
  • 18
  • I think the second line of code should be replaced by `cell.button.superview?.tag = indexPath.section` otherwise, it will show 0 section. – Anurag Sharma Jun 19 '19 at 20:55
2

Swift 2.x - Extension with Associated Objects

private struct AssociatedKeys {
static var section = "section"
static var row = "row"
}

extension UIButton {
var section : Int {
    get {
        guard let number = objc_getAssociatedObject(self,   &AssociatedKeys.section) as? Int else {
            return -1
        }
        return number
    }

    set(value) {
        objc_setAssociatedObject(self,&AssociatedKeys.section,Int(value),objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
}
var row : Int {
    get {
        guard let number = objc_getAssociatedObject(self, &AssociatedKeys.row) as? Int else {
            return -1
        }
        return number
    }

    set(value) {
        objc_setAssociatedObject(self,&AssociatedKeys.row,Int(value),objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
}
}

usage:

//set
cell.contextMenuBtn.section = indexPath.section
cell.contextMenuBtn.row = indexPath.row 
//read
func showContextMenu (sender:UIButton) {
    let track = dictionarySongs[sender.section][sender.row]
    ...
}
iluvatar_GR
  • 1,017
  • 13
  • 19
1

Send Row and Section through in Button you can use tag and accessibilityIdentifier, take a look:

Set value

 button.tag = indexPath.row
 button.accessibilityIdentifier = "\(indexPath.section)"

Get Value

let indexRow = button.tag
let indexSection = Int(button.accessibilityIdentifier!)
Shaheen
  • 189
  • 1
  • 5
0

I suggest another approach. I don't like the subclass solution, I don't think the button should know its position. Why not use a dictionary to translate the button into the IndexPath

// First initialize the lookup table
var buttonPositions = [UIButton: NSIndexPath]()

// add each button to the lookup table
let b = UIButton()
let path = NSIndexPath(forItem: 1, inSection: 1)


buttonPositions[b] = path

// Looking it up works like this
buttonPostions[activeButton]?.row  
Raymond
  • 3,630
  • 2
  • 19
  • 22
0

You can assign a custom tag for a UIButton via extension, take a look:

extension UIButton {

private struct ButtonTag {

    static var tagKey = "key"

}

var myTag : (Int , Int)? {

    set  {
        if let value = newValue {
            objc_setAssociatedObject(self, &ButtonTag.tagKey, value as! AnyObject, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    get {
        return objc_getAssociatedObject(self, &ButtonTag.tagKey) as? (Int , Int)
    }
  }   
}
Meseery
  • 1,845
  • 2
  • 22
  • 19
-1

There are certain possible solution to this question:

Solution 1:

  1. Create a Subclass of UIButton:
class CustomTaggedButton: UIButton {
   var section: Int = 0
   var row: Int = 0
}
  1. Assign CustomTaggedButton to your button.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as! CustomTableViewCell

    cell.button.section = indexPath.section
    cell.button.row = indexPath.row
    button.setTitle("Tap me", for: .normal)
    button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
    
    return cell
}
  1. On button click
func buttonTapped(_ sender: CustomTaggedButton) {
    let section = sender.section
    let row = sender.row
    
    print("Button tapped - Section: \(section), Row: \(row)")
    
    // Use section and row as needed
}

Solution 2: https://stackoverflow.com/a/32352839/4257067 logical or meaningful solution for this specific answer.

use combination of bit shifting and bitwise operations to pack the section and row information into a single tag value.

  1. When assigning tags:
let tag = (indexPath.section << 16) | indexPath.row
cell.button.tag = tag
  1. When extracting section and row:
func buttonTapped(_ sender: UIButton) {
    let tag = sender.tag
    let section = (tag >> 16) & 0xFFFF
    let row = tag & 0xFFFF
        
    print("Button tapped - Section: \(section), Row: \(row)")
    
    // Use section and row as needed
}

Sample tag value from section & row:

Tag for Section 0, Row 0: tag = (0 << 16) | 0 = 0
Tag for Section 1, Row 2: tag = (1 << 16) | 2 = 65538
Tag for Section 2, Row 3: tag = (2 << 16) | 3 = 131075
Vicky Prajapati
  • 137
  • 1
  • 5