Send Row and Section through Tag in Button Swift
Asked Answered
T

8

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.

Thermoluminescence answered 2/9, 2015 at 10:42 Comment(1)
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.Doyen
G
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.

Gariepy answered 2/9, 2015 at 10:47 Comment(1)
See my error above... Not sure why this is happeningThermoluminescence
K
21

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.

Kitten answered 2/9, 2015 at 12:12 Comment(6)
What a nice solution... I was trying to do something along these lines as well. Obviously both section and row are assumed to be IntegersThermoluminescence
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?Gabi
@Gabi 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.Kitten
@Gabi Also note that since we're talking about integers, 2005 / 1000 = 2, not 2.005.Kitten
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.Gabi
@Gabi Modulo is a standard arithmetic operation that returns the remainder. See 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.Kitten
G
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.

Gariepy answered 2/9, 2015 at 10:47 Comment(1)
See my error above... Not sure why this is happeningThermoluminescence
C
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
}
Callaghan answered 25/9, 2018 at 23:9 Comment(1)
I think the second line of code should be replaced by cell.button.superview?.tag = indexPath.section otherwise, it will show 0 section.Ningpo
T
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]
    ...
}
Tread answered 14/10, 2016 at 11:15 Comment(0)
M
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!)
Magnetostriction answered 2/4, 2018 at 16:51 Comment(0)
S
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  
Sylvan answered 2/9, 2015 at 12:31 Comment(0)
V
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)
    }
  }   
}
Viborg answered 5/9, 2016 at 13:23 Comment(0)
F
-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://mcmap.net/q/1321538/-send-row-and-section-through-tag-in-button-swift 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
Farfetched answered 25/8, 2023 at 6:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.