获取按钮单击UITableViewCell内部


140

我有一个具有表视图的视图控制器和一个用于表单元格模板的单独的笔尖。单元格模板具有一些按钮。我想访问按钮单击以及在定义了Table视图的视图控制器内单击的单元格的索引。

所以,我有ViewController.hViewController.m在那里我有UITableViewTableTemplate.hTableTemplate.mTableTemplate.xib在那里我有定义的笔尖。我希望按钮点击事件中带有单元格索引ViewController.m

我该怎么办?

Answers:


258

1)在您的cellForRowAtIndexPath:方法中,将按钮标签分配为索引:

cell.yourbutton.tag = indexPath.row;

2)为按钮添加目标和操作,如下所示:

[cell.yourbutton addTarget:self action:@selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];

3)基于索引的代码动作如下ViewControler

-(void)yourButtonClicked:(UIButton*)sender
{
     if (sender.tag == 0) 
     {
         // Your code here
     }
}

多个部分的更新:

您可以检查此链接以检测表视图中多个行和部分的按钮单击。


1
这也可以在第二步中通过Interface Builder(IB)完成。只需确保已设置按钮标签即可。您确实不想混用行动呼吁。通过IB或在代码中显式进行。
Sententia 2014年

@Mani不会破坏MVC-操作在TableView中而不是Cell中。
davecom 2014年

@davecom如果将按钮目标设置为单元格(通过IB),它将如何从tableView触发?还是他们有什么办法将按钮目标连接到放置在单元格xib中的tableview?
玛尼2014年

24
当您开始插入和删除行时,此解决方案会遇到问题。当行移动时,标记不会更新。而不是保留对该行的引用。最好保留对唯一对象ID的引用。
文森特·张2015年

1
每当您发现自己将值分配给视图的标签属性时,就会产生非常难闻的代码气味,以后可能会咬住您。寻找更好的方法来实现目标,而不是找到的第一篇SO帖子。
TigerCoding 2015年

148

代表是必经之路。

如其他答案所示,使用视图可能会过时。谁知道明天可能还会有另一个包装并且可能需要使用cell superview]superview]superview]superview]。如果使用标签,则最终将以n个if else条件来标识单元格。为了避免所有这些设置代表。(通过这样做,您将创建一个可重用的单元格类。您可以使用与基类相同的单元格类,而您所要做的就是实现委托方法。)

首先,我们需要一个界面(协议),单元将使用该界面来传达(代表)按钮点击。(您可以为协议创建一个单独的.h文件,并将其同时包含在表视图控制器和自定义单元格类中,或者仅将其添加到自定义单元格类中,无论如何将其包含在表视图控制器中

@protocol CellDelegate <NSObject>
- (void)didClickOnCellAtIndex:(NSInteger)cellIndex withData:(id)data;
@end

在自定义单元和表视图控制器中包括此协议。并确保表视图控制器对此协议进行确认。

在自定义单元格中,创建两个属性:

@property (weak, nonatomic) id<CellDelegate>delegate;
@property (assign, nonatomic) NSInteger cellIndex;

UIButtonIBAction委托中,单击:(可以对需要委托回视图控制器的自定义单元格类中的任何操作执行相同的操作

- (IBAction)buttonClicked:(UIButton *)sender {
    if (self.delegate && [self.delegate respondsToSelector:@selector(didClickOnCellAtIndex:withData:)]) {
        [self.delegate didClickOnCellAtIndex:_cellIndex withData:@"any other cell data/property"];
    }
}

cellForRowAtIndexPath单元格出队后,在表格视图控制器中设置以上属性。

cell.delegate = self;
cell.cellIndex = indexPath.row; // Set indexpath if its a grouped table.

并在表视图控制器中实现委托:

- (void)didClickOnCellAtIndex:(NSInteger)cellIndex withData:(id)data
{
    // Do additional actions as required.
    NSLog(@"Cell at Index: %d clicked.\n Data received : %@", cellIndex, data);
}

这将是在表视图控制器中获取自定义单元格按钮动作的理想方法。


2
您为什么要使代表成为该单元的强大属性?除非您知道控制器仅微弱地握住电池,否则这将给您一个保留周期。
朱利安·席姆斯(JulianSymes)2014年

删除单元格后更新的_cellIndex怎么办?
skornos'3

2
我从一位朋友那里听说,在每个单元格上使用委托会导致内存消耗,因此请使用标签。这是真的?
Bista 2015年

2
检查这个问题花花公子stackoverflow.com/questions/31649220/...
Nischal哈达

@the_UB在设置标签和存储单个引用之间没有太多的区别。标签可能会占用更多内存。
伊恩·沃伯顿

66

我没有使用标签,而是采取了不同的方法。为我的UITableViewCell(OptionButtonsCell)子类提供了委托,并添加了indexPath var。从情节提要中的按钮,我将@IBAction连接到OptionButtonsCell,然后将具有正确indexPath的委托方法发送给感兴趣的任何人。在索引路径的单元格中,我设置了当前的indexPath,它是可行的:)

让代码说明一切:

斯威夫特3 Xcode 8

OptionButtonsTableViewCell.swift

import UIKit
protocol OptionButtonsDelegate{
    func closeFriendsTapped(at index:IndexPath)
}
class OptionButtonsTableViewCell: UITableViewCell {
    var delegate:OptionButtonsDelegate!
    @IBOutlet weak var closeFriendsBtn: UIButton!
    var indexPath:IndexPath!
    @IBAction func closeFriendsAction(_ sender: UIButton) {
        self.delegate?.closeFriendsTapped(at: indexPath)
    }
}

MyTableViewController.swift

class MyTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, OptionButtonsDelegate {...

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "optionCell") as! OptionButtonsTableViewCell
    cell.delegate = self
    cell.indexPath = indexPath
    return cell   
}

func closeFriendsTapped(at index: IndexPath) {
     print("button tapped at index:\(index)")
}

您能帮我吗,我在此行遇到错误:class MyTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, OptionButtonsDelegate //错误:“ MyTableViewController”与协议“ UITableViewDataSource”的冗余符合性
Ulug'bek Ro'zimboyev

看起来您正在尝试多次符合UITableViewDataSource。也许您已经有了一个扩展,您已经符合数据源了吗?如果没有代码就
无济于事

1
以及如何传递数据以执行segue并转到另一个视图控制器?
Milad Faridnia'3

2
最好,最干净的解决方案!
appsunited

31

这应该有帮助:-

UITableViewCell* cell = (UITableViewCell*)[sender superview];
NSIndexPath* indexPath = [myTableView indexPathForCell:cell];

这里的sender是发送事件的UIButton实例。 myTableView是您正在处理的UITableView实例。

只需正确获取单元格引用,即可完成所有工作。

您可能需要从单元格的contentView中删除按钮,并将其直接添加到UITableViewCell实例的子视图中。

要么

您可以在cell.contentView中为不同的UIButton制定标签命名方案。使用此标签,以后您可以根据需要了解行和节信息。


4
应该是[[sender superview] superview];
pierre23'2014-4-23

2
这对于非常简单的单元格很有用。但是,如果您的牢房有很深的见解,那么Mani的答案是最好的。
Sententia 2014年

3
现在在iOS 7中,它应该是UITableViewCell * cell =(UITableViewCell *)[[[sender superview] superview] superview]; 谢谢。
Rajan Maharjan 2014年

检查这个问题花花公子stackoverflow.com/questions/31649220/...
Nischal哈达

22

以下代码可能会对您有所帮助。

我已将UITableView自定义原型单元格类命名为UITableViewCellinside UIViewController

所以我有ViewController.hViewController.m并且TableViewCell.hTableViewCell.m

这是该代码:

ViewController.h

@interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>

@property (strong, nonatomic) IBOutlet UITableView *tblView;

@end

ViewController.m

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return (YourNumberOfRows);
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *cellIdentifier = @"cell";

    __weak TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    if (indexPath.row==0) {
        [cell setDidTapButtonBlock:^(id sender)
         {
             // Your code here

         }];
    }    
    return cell;
}

自定义单元格类别:

TableViewCell.h

@interface TableViewCell : UITableViewCell

@property (copy, nonatomic) void (^didTapButtonBlock)(id sender);

@property (strong, nonatomic) IBOutlet UILabel *lblTitle;
@property (strong, nonatomic) IBOutlet UIButton *btnAction;

- (void)setDidTapButtonBlock:(void (^)(id sender))didTapButtonBlock;

@end

UITableViewCell.m

@implementation TableViewCell

- (void)awakeFromNib {
    // Initialization code
    [self.btnAction addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];

}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}
- (void)didTapButton:(id)sender {
    if (self.didTapButtonBlock)
    {
        self.didTapButtonBlock(sender);
    }
}

注意:在这里,我已经全部UIControls使用了Storyboard。

希望可以帮助您... !!!


有史以来最好的方式
Daniel Raouf

15

我之所以喜欢下面的技术,是因为它也可以帮助我识别表的部分。

在单元格cellForRowAtIndexPath中添加按钮:

 UIButton *selectTaskBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [selectTaskBtn setFrame:CGRectMake(15, 5, 30, 30.0)];
        [selectTaskBtn setTag:indexPath.section]; //Not required but may find useful if you need only section or row (indexpath.row) as suggested by MR.Tarun 
    [selectTaskBtn addTarget:self action:@selector(addTask:)   forControlEvents:UIControlEventTouchDown];
[cell addsubview: selectTaskBtn];

事件addTask:

-(void)addTask:(UIButton*)btn
{
    CGPoint buttonPosition = [btn convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    if (indexPath != nil)
    {
     int currentIndex = indexPath.row;
     int tableSection = indexPath.section;
    }
}

希望对您有所帮助。


检查这个问题花花公子stackoverflow.com/questions/31649220/...
Nischal哈达

12

使用Swift闭包:

class TheCell: UITableViewCell {

    var tapCallback: (() -> Void)?

    @IBAction func didTap(_ sender: Any) {
        tapCallback?()
    }
}

extension TheController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: TheCell.identifier, for: indexPath) as! TheCell {
            cell.tapCallback = {
                //do stuff
            }
            return cell
    }
}

7

Tarun的代码在iOS7上不起作用,因为UITableViewCell结构已更改,现在他将获得“ UITableViewCellScrollView”。

这篇文章在iOS 7中使用超级视图获取UITableViewCell具有一个很好的解决方案,它创建了一个循环以查找正确的父视图,而不管将来结构上的任何更改。归结为创建一个循环:

    UIView *superView = [sender superview];
    UIView *foundSuperView = nil;

    while (nil != superView && nil == foundSuperView) {
        if ([superView isKindOfClass:[UITableViewCell class]]) {
            foundSuperView = superView;
        } else {
            superView = superView.superview;
        }
    }

该链接包含用于更可重用的解决方案的代码,但这应该可以工作。


6

斯威夫特2.2

您需要为该按钮添加目标。

myButton.addTarget(self, action: #selector(ClassName.FunctionName(_:), forControlEvents: .TouchUpInside)

FunctionName:已连接 //例如

当然,由于您正在使用它,因此您需要设置该按钮的标签。

myButton.tag = indexPath.row

您可以通过对UITableViewCell进行子类化来实现。在界面生成器中使用它,在该单元格上放置一个按钮,通过插座连接它,然后就可以了。

要在连接的功能中获取标签:

func connected(sender: UIButton) {
    let buttonTag = sender.tag
    // Do any additional setup
}

6

封闭的Swift 3

一个不错的解决方案是在自定义UITableViewCell中使用闭包来回调操作的viewController。

在单元格中:

final class YourCustomCell: UITableViewCell {

    var callbackClosure: (() -> Void)?

    // Configure the cell here
    func configure(object: Object, callbackClosure: (() -> Void)?) {
       self.callbackClosure = callbackClosure
    }


// MARK: - IBAction
extension YourCustomCell {
    @IBAction fileprivate func actionPressed(_ sender: Any) {
        guard let closure = callbackClosure else { return }
        closure()
    }
}

在视图控制器中:Tableview委托

extension YourViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard let cell: YourCustomCell = cell as? YourCustomCell else { return }
        cell.configure(object: object, callbackClosure: { [weak self] in
            self?.buttonAction()
        })
     }
 }

fileprivate extension YourViewController {

    func buttonAction() {
        // do your actions here 
    }
}

5

我发现最简单的方法是将单元格内的按钮归类(Swift 3):

class MyCellInfoButton: UIButton {
    var indexPath: IndexPath?
}

在您的单元格班级中:

class MyCell: UICollectionViewCell {
    @IBOutlet weak var infoButton: MyCellInfoButton!
   ...
}

在表视图或集合视图的数据源中,使单元出队时,为按钮提供其索引路径:

cell.infoButton.indexPath = indexPath

因此,您可以将这些代码放入表视图控制器中:

@IBAction func handleTapOnCellInfoButton(_ sender: MyCellInfoButton) {
        print(sender.indexPath!) // Do whatever you want with the index path!
}

并且不要忘记在Interface Builder中设置按钮的类并将其链接到handleTapOnCellInfoButton函数!


编辑:

使用依赖注入。设置调用闭包:

class MyCell: UICollectionViewCell {
    var someFunction: (() -> Void)?
    ...
    @IBAction func didTapInfoButton() {
        someFunction?()
    }
}

并将闭包注入集合视图的委托的willDisplay方法中:

 func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    (cell as? MyCell)?.someFunction = {
        print(indexPath) // Do something with the indexPath.
    }
}

闭合方法是我见过的最快速的方法。干得好!
克利夫顿拉布鲁姆

5

它为我工作。

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     UIButton *Btn_Play = (UIButton *)[cell viewWithTag:101];
     [Btn_Play addTarget:self action:@selector(ButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)ButtonClicked:(UIButton*)sender {
     CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.Tbl_Name];
     NSIndexPath *indexPath = [self.Tbl_Name indexPathForRowAtPoint:buttonPosition];
}

1
// Add action in cell for row at index path -tableView

cell.buttonName.addTarget(self, action: #selector(ViewController.btnAction(_:)), for: .touchUpInside)

// Button Action

  @objc func btnAction(_ sender: AnyObject) {



        var position: CGPoint = sender.convert(.zero, to: self.tableView)


        let indexPath = self.tableView.indexPathForRow(at: position)
        let cell: UITableViewCell = tableView.cellForRow(at: indexPath!)! as
        UITableViewCell




}

1

快速4:

inside the cellForItemAt ,
   
cell.chekbx.addTarget(self, action: #selector(methodname), for: .touchUpInside)

then outside of cellForItemAt
@objc func methodname()
{
//your function code
}


1

如果要使用闭包将参数值从单元格传递到UIViewController,则

//Your Cell Class
class TheCell: UITableViewCell {

    var callBackBlockWithParam: ((String) -> ()) = {_ in }

//Your Action on button
    @IBAction func didTap(_ sender: Any) {
        callBackBlockWithParam("Your Required Parameter like you can send button as sender or anything just change parameter type. Here I am passing string")
    }
}

//Your Controller
extension TheController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: TheCell.identifier, for: indexPath) as! TheCell {
            cell.callBackBlockWithParam = { (passedParamter) in 

             //you will get string value from cell class
                print(passedParamter)     
      }
            return cell
    }
}

0

@Mani答案很好,但是单元格contentView内的视图标签通常用于其他目的。您可以改用cell的标签(或cell的contentView标签):

1)在您的cellForRowAtIndexPath:方法中,将单元格的标签分配为索引:

cell.tag = indexPath.row; // or cell.contentView.tag...

2)为按钮添加目标和操作,如下所示:

[cell.yourbutton addTarget:self action:@selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];

3)创建返回发件人行的方法(感谢@Stenio Ferreira):

- (NSInteger)rowOfSender:(id)sender
{
    UIView *superView = sender.superview;
    while (superView) {
        if ([superView isKindOfClass:[UITableViewCell class]])
            break;
        else
            superView = superView.superview;
    }

    return superView.tag;
}

4)基于索引的代码操作:

-(void)yourButtonClicked:(UIButton*)sender
{
     NSInteger index = [self rowOfSender:sender];
     // Your code here
}

0

CustomTableCell.h是一个UITableViewCell:

@property (weak, nonatomic) IBOutlet UIButton *action1Button;
@property (weak, nonatomic) IBOutlet UIButton *action2Button;

导入后的MyVC.m:

@interface MYTapGestureRecognizer : UITapGestureRecognizer
@property (nonatomic) NSInteger dataint;
@end

在MyVC.m的“ cellForRowAtIndexPath”内部:

//CustomTableCell 
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

//Set title buttons
[cell.action1Button setTitle:[NSString stringWithString:NSLocalizedString(@"action1", nil)] forState:UIControlStateNormal];
[cell.action2Button setTitle:[NSString stringWithString:NSLocalizedString(@"action2", nil)] forState:UIControlStateNormal];

//Set visibility buttons
[cell.action1Button setHidden:FALSE];
[cell.action2Button setHidden:FALSE];

//Do 1 action
[cell.action1Button addTarget:self action:@selector(do1Action :) forControlEvents:UIControlEventTouchUpInside];

//Do 2 action
MYTapGestureRecognizer *action2Tap = [[MYTapGestureRecognizer alloc] initWithTarget:self action:@selector(do2Action :)];
cancelTap.numberOfTapsRequired = 1;
cancelTap.dataint = indexPath.row;
[cell.action2Button setUserInteractionEnabled:YES];
[cell.action2Button addGestureRecognizer:action2Tap];

MyVC.m:

-(void)do1Action :(id)sender{
//do some action that is not necessary fr data
}

-(void)do2Action :(UITapGestureRecognizer *)tapRecognizer{
MYTapGestureRecognizer *tap = (MYTapGestureRecognizer *)tapRecognizer;
numberTag = tap.dataint;
FriendRequest *fr = [_list objectAtIndex:numberTag];

//connect with a WS o do some action with fr data

//actualize list in tableView
 [self.myTableView reloadData];
}

-1
cell.show.tag=indexPath.row;
     [cell.show addTarget:self action:@selector(showdata:) forControlEvents:UIControlEventTouchUpInside];

-(IBAction)showdata:(id)sender
{
    UIButton *button = (UIButton *)sender;

    UIStoryboard *storyBoard;
    storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    SecondViewController *detailView = [storyBoard instantiateViewControllerWithIdentifier:@"SecondViewController"];

    detailView.string=[NSString stringWithFormat:@"%@",[_array objectAtIndex:button.tag]];

    [self presentViewController:detailView animated:YES completion:nil];

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.