Shawn Baek
Shawn Baek

Reputation: 1967

How can I get indexPath.row in cell.swift

I have 2 files.

Can I get the indexPath.row in myTabelCell.swift function?

Here is myTableCell.swift

import UIKit
import Parse
import ActiveLabel

class myTableCell : UITableViewCell {

    //Button
    @IBOutlet weak var commentBtn: UIButton!
    @IBOutlet weak var likeBtn: UIButton!
    @IBOutlet weak var moreBtn: UIButton!


    override func awakeFromNib() {
        super.awakeFromNib()


    }

    @IBAction func likeBtnTapped(_ sender: AnyObject) {

        //declare title of button
        let title = sender.title(for: UIControlState())

        //I want get indexPath.row in here!

    }

Here is myTableViewController.swift

class myTableViewController: UITableViewController {

    //Default func
    override func viewDidLoad() {
        super.viewDidLoad()

        //automatic row height
        tableView.estimatedRowHeight = 450
        tableView.rowHeight = UITableViewAutomaticDimension



    }

 // cell config
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


        //define cell
        let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexPath) as! myTableCell



 }

As you can see... I'm trying to get indexPath.row in myTableCell, liktBtnTapped function.

Could you let me know how can I access or get IndexPath.row?

Upvotes: 32

Views: 74962

Answers (9)

Callam
Callam

Reputation: 11539

I have created a UIResponder extension with a recursive method that you can use in any UIView (which inherits from UIResponder) to find a parent view of a specific type.

import UIKit

extension UIResponder {
    /**
     * Returns the next responder in the responder chain cast to the given type, or
     * if nil, recurses the chain until the next responder is nil or castable.
     */
    func next<U: UIResponder>(of type: U.Type = U.self) -> U? {
        return self.next.flatMap({ $0 as? U ?? $0.next() })
    }
}

Using this, we can extend UITableViewCell with some convenient read-only computed properties for the table view and index path of the cell.

extension UITableViewCell {
    var tableView: UITableView? {
        return self.next(of: UITableView.self)
    }

    var indexPath: IndexPath? {
        return self.tableView?.indexPath(for: self)
    }
}

Here is how you could use it in your example:

@IBAction func likeBtnTapped(_ sender: AnyObject) {
    //declare title of button
    let title = sender.title(for: UIControlState())

    //I want get indexPath.row in here!
    self.indexPath.flatMap { print($0) }
}

Upvotes: 46

Ivan
Ivan

Reputation: 1444

Swift 5:

        if 
            let collectionView = superview as? UICollectionView, 
            let index = collectionView.indexPath(for: self) 
        {
            // stuff
        }

Upvotes: 0

Rakesha Shastri
Rakesha Shastri

Reputation: 11242

Swift 4+

Try this inside your cell.

func getIndexPath() -> IndexPath? {
    guard let superView = self.superview as? UITableView else {
        print("superview is not a UITableView - getIndexPath")
        return nil
    }
    indexPath = superView.indexPath(for: self)
    return indexPath
}

Upvotes: 27

Reimond Hill
Reimond Hill

Reputation: 4760

Another Approach for Swift 4.2 and not assuming Superview will be always a tableview

extension UITableViewCell{

    var tableView:UITableView?{
        return superview as? UITableView
    }

    var indexPath:IndexPath?{
        return tableView?.indexPath(for: self)
    }

}

Usage example

@IBAction func checkBoxAction(_ sender: UIButton) {

        guard let indexPath = indexPath else { return }

        sender.isSelected = !sender.isSelected
        myCustomCellDelegate?.checkBoxTableViewCell(didSelectCheckBox: sender.isSelected, for: indexPath)

}

Upvotes: 6

Gurjinder Singh
Gurjinder Singh

Reputation: 10329

Swift 4.1. Here I created function to get IndexPath. Just pass your UIView(UIButton,UITextField etc) and UITableView object to get IndexPath.

func getIndexPathFor(view: UIView, tableView: UITableView) -> IndexPath? {

            let point = tableView.convert(view.bounds.origin, from: view)
            let indexPath = tableView.indexPathForRow(at: point)
            return indexPath
 }

Upvotes: 3

ossamacpp
ossamacpp

Reputation: 686

My solution was subclassing UITableViewCell, so can add IndexPath property. assign custom class for table view cell in storyboard. assign IndexPath value when rowAtIndexPath called.

class MyTableViewCell: UITableViewCell {

    var indexPath: IndexPath?
}

custom class

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    var cell = tableView.dequeueReusableCell(withIdentifier: "cellid1", for: indexPath)
    (cell as? MyTableViewCell)?.indexPath = indexPath
    return cell
}

Upvotes: 1

Heres another way of doing it

import UIKit
import Parse
import ActiveLabel

class myTableCell : UITableViewCell {

//Button
@IBOutlet weak var commentBtn: UIButton!
@IBOutlet weak var likeBtn: UIButton!
@IBOutlet weak var moreBtn: UIButton!


override func awakeFromNib() {
    super.awakeFromNib()


}


}


class myTableViewController: UITableViewController {

//Default func
//assuming you have an array for your table data source
var arrayOfTitles = [String]()
override func viewDidLoad() {
    super.viewDidLoad()

    //automatic row height
    tableView.estimatedRowHeight = 450
    tableView.rowHeight = UITableViewAutomaticDimension



}

// cell config
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


//define cell
let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexPath) as! myTableCell
cell.commentBtn.tag = indexPath.row
cell.commentBtn.addTarget(self, action: #selector(likeBtnTapped(_:), forControlEvents:.TouchUpInside)


//cell config end


@IBAction func likeBtnTapped(sender: UIButton) {
let btn = sender
let indexP = NSIndexPath(forItem: btn.tag, inSection: 0)
let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexP) as! myTableCell

    //I want get indexPath.row in here!
    let title = arrayOfTitles[indexP.row]

    //declare title of button
    cell.commentBtn.setTitle(title, forState: UIControlState.Normal)



}


}

Upvotes: 1

vadian
vadian

Reputation: 285270

Create a property indexPath in the cell class and set it in cellForRowAtIndexPath when the cell is reused.

But there is a caveat: Some table view methods to rearrange the cells don't call cellForRowAtIndexPath. You have to consider this case.

But if you use always only reloadData() it's safe and pretty easy.


Another way is to put the code regarding controlling things back in the controller class and run it via callback closures capturing the index path.

Upvotes: 1

Tarvo M&#228;esepp
Tarvo M&#228;esepp

Reputation: 4533

Easy.. You can do like this inside button action:

let section = 0
let row = sender.tag
let indexPath = IndexPath(row: row, section: section)
let cell: myTableCell = self.feedTableView.cellForRow(at: indexPath) as! myTableCell

And afterwards in cellForRowAtIndexPath:

// add the row as the tag
cell.button.tag = indexPath.row

Upvotes: 9

Related Questions