3

I have created a custom TableViewCell and currently have a button placed in the cell. When the button is pressed, In the tableviewcell.swift file, IBAction func gets executed. I cannot figure out how to determine the index path of the cell that the button is in that is pressed. I was trying to use the following

    @IBAction func employeeAtLunch(sender: AnyObject) {

    let indexPath = (self.superview as! UITableView).indexPathForCell(self)
    println("indexPath?.row")
}

but I get the following error on click: Could not cast value of type 'UITableViewWrapperView' to 'UITableView'

Any help on how to access the index path of the cell?

1
  • You should create an outlet and connect it to your tableView IBOutlet var tableView: UITableView! (you will need to delete the overrides from the methods) Commented Apr 18, 2015 at 20:47

3 Answers 3

5

You are just assuming that the cell's immediate superview is the table view - wrongly. There is no particular reason why that should be so (and indeed it is not). Work with fewer assumptions! You need to keep walking up the superview chain until you do reach the table, like this:

var v : UIView = self
do { v = v.superview! } while !(v is UITableView)

Now v is the table view, and you can proceed to work out what row this is.

What I would actually do, however, is work my up, not from the cell to the table, but from the button to the cell. The technique is exactly the same:

var v : UIView = sender as! UIView
do { v = v.superview! } while !(v is UITableViewCell)

Do that the button's action method, where sender is the button. If the target of the action method is the table view controller, it has access to the table, and the problem is solved.

Sign up to request clarification or add additional context in comments.

4 Comments

I'm not sure it's generally safe to rely on the view hierarchy for these tasks, as internals might change anytime. The last notable change was between iOS 6 & 7( example), and I remember that it caused me lots of trouble.
But that's my point. We know that this thing is a subview of that thing at some level, and that's all we know. Walking up in a non-assuming way like I do here never breaks.
Okay, on a second read, I see your point. :) And working with fewer assumptions is definitely a sound advice. Still not the way I would approach this particular problem though.
@JózsefVesza Well, that's fine. There's more than one way to approach. That doesn't make my way wrong. I've been doing it this way since iOS 3.2 and it has never broken. Walking up hierarchies is a major technique in iOS programming.
4

You could subclass UIButton in your cell with a property for its row.

class MyButton: UIButton {
    var row: Int?
}

Then when you set up your table view, in the cellForRowAtIndexPath method, you set the row property:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        // ...
        cell.button.row = indexPath.row
        // ...
    }

This way when the action fires, you can get the correct row:

@IBAction func employeeAtLunch(sender: MyButton) {
    if let row = sender.row {
        // access the row
    }
}

1 Comment

you meant var row: Int?
0

In your situation, I will add a tag to your button to identify in which row it is. Whenever I configure cell in the call-back cellForRowAtIndexPath, I update this tag value.

When a button clicked, the handler specifies always the button pressed. With the tag defined to that pressed button, you can know the button of which row is pressed.

@IBAction func buttonPressed(sender: AnyObject) {
      //convert to UIButton
      if let btn = sender as? UIButton {
           let rowId = btn.tag
           //do your works
      }
}

If your tableview has more than 1 sections, you will have to setup the value of tags the right way.

The second solution which is better: get the position of the button in your tableView, then get indexpath of that position in your tableview:

let position = sender.convertPoint(CGPointZero, toView: self.tblMain)
let indexPath = self.tblMain.indexPathForRowAtPoint(position)

3 Comments

Excuse-me, it's a lower t.
Yes, I see what you want to say. I was confused with C#. Just fixed that. I've added also another solution
While using the tag property of UIView is a quick, and it does the job, it's not considered a good practice, as it's generally used for tracking views. Furthermore, as the codebase gets more and more complicated, it gets harder to track such arbitrary decisions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.