Send Row and Section through Tag in Button Swift

9.1k Views Asked by At

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.

8

There are 8 best solutions below

1
On BEST ANSWER

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.

1
On

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
}
0
On

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!)
6
On

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.

0
On

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  
0
On

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]
    ...
}
0
On

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
0
On

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)
    }
  }   
}