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:

  • Parse.com PFGeoPoint.geoPointForCurrentLocationInBackground not doing anything
  • Swift - UITableView inside UIViewController, UITableView functions are not called
  • How to do multithreading, concurrency or parallelism in iOS Swift?
  • How to update each custom tableview cell data at the same time?
  • Function to run a wait action at decreasing intervals indefinitely
  • Passing an NSManagedObject Effectively through Storyboard
  • 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)!
    

    5 Solutions Collect From Internet About “Find the indexPath of a button inside UITableViewCell when button pressed?”

    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.

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

    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.

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

    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
    }