3

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)!

6 Answers6

10

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
    }
}
mustafa
  • 15,254
  • 10
  • 48
  • 57
  • Thanks for quick reply, but "tableView.indexPathForRowAtPoint(point)" and "tableView.indexPathForRowAtPoint(point)" still return nil to me. In my case,'table' is connected via IBOutlet and button is a subview of section(TableViewCell) – Arsalan Ansari Nov 25 '14 at 08:14
1

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.

vacawama
  • 150,663
  • 30
  • 266
  • 294
0

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)")
}
Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117
  • Thanks for quick reply, but "table.indexPathForCell(cell)" still return nil to me. In my case,'table' is connected via IBOutlet and button is a subview of section(TableViewCell). – Arsalan Ansari Nov 25 '14 at 08:12
0

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
}
ntoonio
  • 3,024
  • 4
  • 24
  • 28
0

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

-1

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.

vacawama
  • 150,663
  • 30
  • 266
  • 294
  • Thanks for quick reply, i applied your solution. It works for me. But like to have a proper alternative working solution on it. – Arsalan Ansari Nov 25 '14 at 08:15
  • No, using a tag based on the indexPath won't work if any rows can be added or removed. – rmaddy Dec 11 '15 at 06:09
  • @rmaddy. Good point. I updated my answer to note that. Since the other answers weren't working for OP, I took a completely different approach in a new answer. Since my original answer was accepted by OP, I didn't want to edit it and give the impression that my new answer had been accepted. – vacawama Dec 11 '15 at 14:45