By Steve Rowe


2015-04-28 07:31:43 8 Comments

I have an app that has a custom button in a custom cell. If you select the cell it segues to the a detail view, which is perfect. If I select a button in a cell, the code below prints the cell index into the console.

I need to access the contents of the selected cell (Using the button) and add them to an array or dictionary. I am new to this so struggling to find out how to access the contents of the cell. I tried using didselectrowatindexpath, but I don't know how to force the index to be that of the tag...

So basically, if there are 3 cells with 'Dog', 'Cat', 'Bird' as the cell.repeatLabel.text in each cell and I select the buttons in the rows 1 and 3 (Index 0 and 2), it should add 'Dog' and 'Bird' to the array/dictionary.

    // MARK: - Table View

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return postsCollection.count
}

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

    let cell: CustomCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomCell

    // Configure the cell...
    var currentRepeat = postsCollection[indexPath.row]
    cell.repeatLabel?.text = currentRepeat.product
    cell.repeatCount?.text = "Repeat: " + String(currentRepeat.currentrepeat) + " of " + String(currentRepeat.totalrepeat)

    cell.accessoryType = UITableViewCellAccessoryType.DetailDisclosureButton

    cell.checkButton.tag = indexPath.row;

    cell.checkButton.addTarget(self, action: Selector("selectItem:"), forControlEvents: UIControlEvents.TouchUpInside)


    return cell

}

func selectItem(sender:UIButton){

    println("Selected item in row \(sender.tag)")

 }

7 comments

@MLBDG 2017-04-14 22:54:37

XCODE 8: Important Note

Do not forget to set the tags to a different value than 0.

If you attempt to cast an object with tag = 0 it might work but in some weird cases it doesn't.

The fix is to set the tags to different values. Hope it helps someone.

@Herr der Töne 2017-03-07 17:27:06

I updated option 1 of the answer from Vasil Garov for Swift 3

1. Create a protocol for your CustomCell:

protocol CustomCellDelegate {
    func cellButtonTapped(cell: CustomCell)
} 

2. For your TableViewCell declare a delegate variable and call the protocol method on it:

var delegate: CustomCellDelegate?

@IBAction func buttonTapped(sender: AnyObject) {
    delegate?.cellButtonTapped(self)
}

3. Conform to the CustomCellDelegate in the class where your tableView is:

 class ViewController: CustomCellDelegate

4. Set your cell's delegate

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)  as CustomCell
    cell.delegate = self

    return cell
}

5. Implement the required method in your ViewController.

@Shailesh Talaviya Php Freelanc 2017-01-03 12:45:44

Swift 3 I just get solution for access cell in @IBAction function using superview of button tag.

let cell = sender.superview?.superview as! ProductCell
var intQty = Int(cell.txtQty.text!);
intQty = intQty! + 1
let  strQty = String(describing: intQty!);
cell.txtQty.text = strQty

@CyrIng 2016-04-03 07:53:27

Based on Cao's answer, here is a solution to handle buttons in a collection view cell.

    @IBAction func actionButton(sender: UIButton) {
        let point = collectionView.convertPoint(sender.center, fromView: sender.superview)
        let indexPath = collectionView.indexPathForItemAtPoint(point)
}

Be aware that the convertPoint() function call will translate the button point coordinates in the collection view space. Without, indexPath will always refer to the same cell number 0

@Cao Yong 2015-06-22 15:08:40

@IBAction func buttonTap(sender: UIButton) {
        let button = sender as UIButton
        let indexPath = self.tableView.indexPathForRowAtPoint(sender.center)!
}

@Vasil Garov 2015-04-28 13:12:36

OPTION 1. Handling it with delegation

The right way of handling events fired from your cell's subviews is to use delegation.

So you can follow the steps:

1. Above your class definition write a protocol with a single instance method inside your custom cell:

protocol CustomCellDelegate {
    func cellButtonTapped(cell: CustomCell)
} 

2. Inside your class definition declare a delegate variable and call the protocol method on the delegate:

var delegate: CustomCellDelegate?

@IBAction func buttonTapped(sender: AnyObject) {
    delegate?.cellButtonTapped(self)
}

3. Conform to the CustomCellDelegate in the class where your table view is:

 class ViewController: CustomCellDelegate

4. Set your cell's delegate

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomCell
    cell.delegate = self

    return cell
}

5. Implement the required method in your view controller class.

EDIT: First define an empty array and then modify it like this:

private var selectedItems = [String]()

func cellButtonTapped(cell: CustomCell) {
    let indexPath = self.tableView.indexPathForRowAtPoint(cell.center)!
    let selectedItem = items[indexPath.row]

    if let selectedItemIndex = find(selectedItems, selectedItem) {
        selectedItems.removeAtIndex(selectedItemIndex)
    } else {
        selectedItems.append(selectedItem)
    }
}

where items is an array defined in my view controller:

private let items = ["Dog", "Cat", "Elephant", "Fox", "Ant", "Dolphin", "Donkey", "Horse", "Frog", "Cow", "Goose", "Turtle", "Sheep"] 

OPTION 2. Handling it using closures

I've decided to come back and show you another way of handling these type of situations. Using a closure in this case will result in less code and you'll achieve your goal.

1. Declare a closure variable inside your cell class:

var tapped: ((CustomCell) -> Void)?

2. Invoke the closure inside your button handler.

@IBAction func buttonTapped(sender: AnyObject) {
    tapped?(self)
}

3. In tableView(_:cellForRowAtIndexPath:) in the containing view controller class :

let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CustomCell       
cell.tapped = { [unowned self] (selectedCell) -> Void in
    let path = tableView.indexPathForRowAtPoint(selectedCell.center)!
    let selectedItem = self.items[path.row]

    println("the selected item is \(selectedItem)")
}

@Andrew Robinson 2015-04-28 13:49:23

I like this solution better than the one above. Though, you forgot to mention that he has to actually set the delegate. Other than that the solution is solid.

@Vasil Garov 2015-04-28 13:54:15

Of course I should have, my bad. Will edit it. Thanks.

@Steve Rowe 2015-04-29 19:46:55

Thanks so much guys. It works like a charm and has given me a little insight into delegate.. still very much a newbie.

@Steve Rowe 2015-05-01 21:02:51

I know I am pushing it now but I have been going around in circles.. I need to be able to add the selectedItem to an array and I have no idea how. I've tried adding it to the cellButtonTapped fund, but that doesn't work. If I select something, it must add it to the array, if I unselect it, it must remove it from the array. The idea is that once I have the array I am going to post the results of the selected items to a mySQL database when the user clicks a 'Send' button...

@Vasil Garov 2015-05-01 22:46:51

What did you try in cellButtonTapped ? And what is the expected behavior exactly - tapping the button -> add item to the array, tapping it again -> remove item from the array?

@Steve Rowe 2015-05-02 08:38:28

I tried something like var selectedItems : [String] = String and then appending each selection to the array. This just adds the currently selected item to the array. What you described is exactly what I need it to do. To get the initial data from the mySQL database I have Post class (with JSON) that gets used to create the array postCollection. I suppose I need to create the reverse of that to get to the point where I can send what was selected back to the mySQL database..

@Vasil Garov 2015-05-02 08:59:45

OK, I will edit my answer. Note that I am not on my Mac and I can't test the code.

@Steve Rowe 2015-05-02 11:16:17

Thanks so much. You are a star. It worked perfectly.

@seggy 2016-12-09 09:48:41

@flashadvanced thank you so much its worked perfectly.. but how i change image of button using option 2 ?

@seggy 2016-12-09 10:14:32

selft solved thank you again Happy Coding :-)

@Akhilesh Sharma 2017-01-10 09:15:43

I have used a variable in custom cell class,if i alter the variable on button action using delegate method on another view, than how do i get the reflected value of that variable in cellforitem.

@Amit89 2015-04-28 07:45:30

Since you have 1 section in the table view you can get the cell object as below.

let indexPath = NSIndexPath(forRow: tag, inSection: 0)
let cell = tableView.cellForRowAtIndexPath(indexPath) as! CustomCell!

where tag you will get from button tag.

@Jay Patel 2017-07-12 05:20:32

it will not work everytime even if its one section.suppose you deleted one cell from tableview and if you access tag as indexPath it will give you wrong index.so beware of that...

Related Questions

Sponsored Content

3 Answered Questions

[SOLVED] Expand and Collapse tableview cells

9 Answered Questions

[SOLVED] How to detect tableView cell touched or clicked in swift

1 Answered Questions

[SOLVED] Swift vertical UICollectionView inside UITableView

3 Answered Questions

1 Answered Questions

[SOLVED] TableView not displaying text with JSON data from API call

1 Answered Questions

[SOLVED] thread 1: exc_bad_instruction(code=exc_1386_invop,subcode=0x0)

  • 2016-10-03 14:11:52
  • HarryHuang
  • 245 View
  • 1 Score
  • 1 Answer
  • Tags:   ios swift

2 Answered Questions

1 Answered Questions

[SOLVED] Prepare for segue delay. Information is lagging but being presented

3 Answered Questions

[SOLVED] Sending data to a new view controller through didselectrowatindexpath

  • 2016-01-26 18:39:57
  • Dan Levy
  • 914 View
  • 1 Score
  • 3 Answer
  • Tags:   swift

1 Answered Questions

[SOLVED] Custom Cell Image as AccessoryType in Swift

Sponsored Content