UITableViewCell Buttons with action

2019-02-02 17:29发布

Hi I have a custom UITableViewCell with three buttons to handle a shopping cart function, Plus,Minus and Delete button and I need to know which cell has been touched.

I've already tried to use the "tag solution" but it isn't working due to the lifecycle of the cells.

Can anyone please help me find a solution?

Thanks in advance

5条回答
唯我独甜
2楼-- · 2019-02-02 17:48

You can also get selected button index using tableViewCell view's hierarchy.

Using following steps :

  1. add selector to the cellForRowAtIndexpath of tableview :

    btn?.addTarget(self, action:#selector(buttonPressed(_:)), for:.touchUpInside)
    

2 . get indexPath using following method :

func buttonPressed(_ sender: AnyObject) {
        let button = sender as? UIButton
        let cell = button?.superview?.superview as? UITableViewCell
        let indexPath = tblview.indexPath(for: cell!)
        print(indexPath?.row)
    }
查看更多
ら.Afraid
3楼-- · 2019-02-02 17:49

@pedrouan is great, except using button's tag option. In many cases, when you set button on tableViewCell, those buttons will modify tableView dataSource.(e.g. InsertRow, DeleteRow).

But the tag of the button is not updated even if a new cell is inserted or deleted. Therefore, it is better to pass the cell itself as a parameter rather than passing the button's tag to the parameter.

Here is my example to achieve this.

Your ExampleCell

protocol ExampleCellDelegate: class {
    func didTapButton(cell: ExampleCell)
}

class ExampleCell: UITableViewCell {

    weak var cellDelegate: ExampleCellDelegate?

    @IBAction func btnTapped(_ sender: UIButton) {
        cellDelegate?.didTapButton(cell: self)
    }
}

Your ViewController

class ViewController: ExampleCellDelegate {
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: "ExampleCell", for: indexPath) as? ExampleCell {

            cell.cellDelegate = self
            return cell
        }
        return UITableViewCell()
    }

    func didTapButton(cell: ExampleCell) {
        if let indexPath = tableView.indexPath(for: cell) {
            // do Something
        }
    }
}
查看更多
萌系小妹纸
4楼-- · 2019-02-02 17:53

I was resolving this using a cell delegate method within UITableViewCell's subclass.

Quick overview:

1) Create a protocol

protocol YourCellDelegate : class {
    func didPressButton(_ tag: Int)
}

2) Subclass your UITableViewCell (if you haven't done so):

class YourCell : UITableViewCell
{
     var cellDelegate: YourCellDelegate?   
      @IBOutlet weak var btn: UIButton!
    // connect the button from your cell with this method
    @IBAction func buttonPressed(_ sender: UIButton) {
        cellDelegate?.didPressButton(sender.tag)
    }         
    ...
}

3) Let your view controller conform to YourCellDelegate protocol that was implemented above.

class YourViewController: ..., YourCellDelegate {  ... }

4) Set a delegate, after the cell has been defined (for reusing).

let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! YourCell
cell.cellDelegate = self
cell.btn.tag = indexPath.row

5) In the same controller (where is your implemented UITableView delegate/datasource), put a method from YourCellDelegate protocol.

func didPressButton(_ tag: Int) {
     print("I have pressed a button with a tag: \(tag)")
}

Now, your solution is not tag / number dependent. You can add as many buttons as you want, so you are ready to get response via delegate regardless how many buttons you want to install.

This protocol-delegate solution is preferred in iOS logic and it can be used for other elements in table cell, like UISwitch, UIStepper, and so on.

查看更多
家丑人穷心不美
5楼-- · 2019-02-02 17:56

I came across the same problem after making the IBOutlets private as has been broadly suggested by the community.

Here is my solution:

< In your cell class >

protocol YourCellDelegate: class {
    func didTapButton(_ sender: UIButton)
}

class YourCell: UITableViewCell {

    weak var delegate: YourCellDelegate?

    @IBAction func buttonTapped(_ sender: UIButton) {
        delegate?.didTapButton(sender)
    }
}

< In your ViewController >

class ViewController: UIViewController, YourCellDelegate {

    func didTapButton(_ sender: UIButton) {
        if let indexPath = getCurrentCellIndexPath(sender) {
            item = items[indexPath.row]
        }
    }

    func getCurrentCellIndexPath(_ sender: UIButton) -> IndexPath? {
        let buttonPosition = sender.convert(CGPoint.zero, to: tableView)
        if let indexPath: IndexPath = tableView.indexPathForRow(at: buttonPosition) {
            return indexPath
        }
        return nil
    }
}
查看更多
ら.Afraid
6楼-- · 2019-02-02 17:58

SWIFT 4.*

It can be done like following way too, Not required much coding and delegation, Simple and easy.

Put following code in cellForItemAt for UICollectionView or in cellForRowAt for UITableView

cell.btn.tag = indexPath.row
cell.btn.addTarget(self, action: #selector(buttonSelected), for: .touchUpInside)

And your Method will be

@objc func buttonSelected(sender: UIButton){
    print(sender.tag)
}

Thats all.

查看更多
登录 后发表回答