Arsalan Ansari
Arsalan Ansari

Reputation: 83

Find the indexPath of a button inside UITableViewCell when button pressed?

The code below correctly returns the cell:

func findSuperView(sender:UIButton!) -> UITableViewCell { 
    var superView : UIView? = sender.superview 
    var foundSuperView : UITableViewCell! 

    while superView != nil && foundSuperView == nil { 
        if let cell = superView as? UITableViewCell { 
            foundSuperView = cell 
            break 
        } 
        else { 
            superView = superView?.superview 
        } 
    } 
    return foundSuperView 
}

But for finding indexpath in tableview it crashes:

var indexPath : NSIndexPath = self.table .indexPathForCell(findSuperView(sender))!
println("Section (indexPath)")

And I tried another way, but it was not successful:

var button : UIButton = sender as UIButton; 
var touch: UITouch = events .allTouches()?.anyObject() as UITouch 
var location : CGPoint = touch.locationInView(self.table) 
var indexPath : NSIndexPath = self.table.indexPathForRowAtPoint(location)!

Upvotes: 3

Views: 3817

Answers (6)

This code works fine for Swift 5:

private func onTapButtonInsideCell(button: UIButton, event: UIEvent) {

    guard let touches = event.touches(for: button), !touches.isEmpty else {
        return
    }

    let touch = touches[touches.startIndex]
    let point = touch.location(in: tableView)

    guard let indexPath = tableView.indexPathForItem(at: point) else {
        return
    }

    print("onTapButtonInsideCell() indexPath.row=\(indexPath.row)")
}

Github Gist: https://gist.github.com/rsaenzi/13dc00b5cb5a09efa84eaff3ff4682af

Upvotes: 0

ntoonio
ntoonio

Reputation: 3143

If you're using a custom tableViewCell (which you probably are) you can just create a variable.

class Cell: UITableViewCell {

    var id = ""

    @IBAction func buttonPressed(sender: AnyObject) {
        print(id) // Can also call a function with 'id' as a parameter
    }
}

And:

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

    cell.id = indexPath.row // Or however you wan't to give it an Id

    return cell
}

Upvotes: 0

vacawama
vacawama

Reputation: 154711

You seem to be having trouble finding the tableView from your code which handles the @IBAction for your button.

You could create a UIButton subclass that keeps track of both the cell the button is in and the UITableView that the cell is contained in. Then it is a simple matter of calling tableView:indexPathForCell in the @IBAction for the button.

MyButton.swift:

class MyButton: UIButton {
    weak var myTable: UITableView?
    weak var myCell:  UITableViewCell?
}

CustomTableViewCell.swift:

class CustomTableViewCell: UITableViewCell {
    @IBOutlet weak var myButton: MyButton!

    @IBAction func whereAmI(button: MyButton) {
        if let myCell = button.myCell, indexPath = button.myTable?.indexPathForCell(myCell) {
            print("I am in row \(indexPath.row)")
        }
    }
}

In TableViewController.swift:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CustomTableViewCell

    cell.myButton.myCell = cell
    cell.myButton.myTable = tableView

    // Other cell setup

    return cell
}

To make this work, it is important to set the classes for the UIButton and the UITableViewCell to MyButton and CustomTableViewCell in the Identity Inspector. Also, wire the button to its @IBOutlet in CustomTableViewCell.swift.

Upvotes: 1

vacawama
vacawama

Reputation: 154711

I don't know if there is an easy a way to do this. (Edit: Actually there is. Look at @mustafa's second solution.) A workaround is to set the button's tag to indexPath.row in cellForRowAtIndexPath, then you can just access the button's tag to find out which row it belongs to.

Warning: This workaround is fragile. It won't work correctly if you allow rows to be added or deleted from your table without then calling tableView.reloadData(). Look at @mustafa's solution which is much more robust.

Upvotes: -1

mustafa
mustafa

Reputation: 15464

Here is a candidate action method for your button's TouchUpInside event.

func someAction(sender:UIButton, event: UIEvent) {
    if let touch = event.touchesForView(sender)?.anyObject() as? UITouch {
        let point = touch.locationInView(tableView)
        if let indexPath = tableView.indexPathForRowAtPoint(point) {
            // Do something with indexPath
        }
    }
}

And here is another one:

func someAction(sender: UIButton) {
    let point = tableView.convertPoint(CGPointZero, fromView: sender)
    if let indexPath = tableView.indexPathForRowAtPoint(point) {
        // Do something with indexPath
    }
}

Upvotes: 10

Jeffery Thomas
Jeffery Thomas

Reputation: 42598

There is an issue with func findSuperView(sender:UIButton!) -> UITableViewCell. Nothing ensures foundSuperView will have a value.

func findSuperView(sender:UIButton!) -> UITableViewCell { 
    var superView : UIView? = sender.superview 
    var foundSuperView : UITableViewCell! // NOTE: The value is nil.

    while superView != nil && foundSuperView == nil { 
        if let cell = superView as? UITableViewCell { 
            foundSuperView = cell 
            break 
        } 
        else { 
            superView = superView?.superview 
        } 
    } 
    return foundSuperView // NOTE: This will crash if foundSuperView == nil
}

A safer way of finding the super cell of a view is returning an optional.

func findSuperCellOfView(view: UIView?) -> UITableViewCell? {
    if view == nil {
        return nil
    } else if let cell = view as? UITableViewCell {
        return cell
    } else {
        return findSuperCellOfView(view?.superview)
    }
}

Which would be used as follows.

if let cell = findSuperCellOfView(button) {
    let indexPath = table.indexPathForCell(cell)
    println("Section \(indexPath)")
}

Upvotes: 0

Related Questions