简体   繁体   English

在 UITableViewCell 中获取按钮单击

[英]Get button click inside UITableViewCell

I have a view controller with a table view and a separate nib for the table cell template.我有一个带有表格视图的视图控制器和一个用于表格单元格模板的单独笔尖。 The cell template has some buttons.单元格模板有一些按钮。 I want to access the button click along with the index of the cell clicked inside the view controller where I have defined the Table view.我想访问按钮单击以及在我定义表视图的视图控制器内单击的单元格的索引。

So I have ViewController.h and ViewController.m where I have the UITableView and TableTemplate.h , TableTemplate.m and TableTemplate.xib where I have the nib defined.所以我有ViewController.hViewController.m ,我有UITableViewTableTemplate.hTableTemplate.mTableTemplate.xib在那里我定义了笔尖。 I want the button click event with cell index in ViewController.m .我想要ViewController.m带有单元ViewController.m引的按钮单击事件。

Any help on how can I do that?关于我该怎么做的任何帮助?

1) In your cellForRowAtIndexPath: method, assign button tag as index: 1) 在你的cellForRowAtIndexPath:方法中,将按钮标签指定为索引:

cell.yourbutton.tag = indexPath.row;

2) Add target and action for your button as below: 2)为您的按钮添加目标和操作,如下所示:

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

3) Code actions based on index as below in ViewControler : 3) 在ViewControler基于索引的代码操作如下:

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

Updates for multiple Section:多个部分的更新:

You can check this link to detect button click in table view for multiple row and section.您可以检查此链接以检测表视图中多行和部分的按钮单击。

Delegates are the way to go.代表是要走的路。

As seen with other answers using views might get outdated.正如使用视图的其他答案所见,可能会过时。 Who knows tomorrow there might be another wrapper and may need to use cell superview]superview]superview]superview] .谁知道明天可能会有另一个包装器并且可能需要使用cell superview]superview]superview]superview] And if you use tags you would end up with n number of if else conditions to identify the cell.如果你使用标签,你最终会得到 n 个 if else 条件来识别单元格。 To avoid all of that set up delegates.为了避免所有这些设置代表。 (By doing so you will be creating a re usable cell class. You can use the same cell class as a base class and all you have to do is implement the delegate methods.) (通过这样做,您将创建一个可重用的单元类。您可以使用相同的单元类作为基类,您所要做的就是实现委托方法。)

First we need a interface (protocol) which will be used by cell to communicate(delegate) button clicks.首先,我们需要一个接口(协议),它将被单元用来通信(委托)按钮点击。 ( You can create a separate .h file for protocol and include in both table view controller and custom cell classes OR just add it in custom cell class which will anyway get included in table view controller ) 您可以为协议创建一个单独的 .h 文件并包含在表格视图控制器和自定义单元格类中,或者只需将其添加到自定义单元格类中,无论如何都会包含在表格视图控制器中

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

Include this protocol in custom cell and table view controller.将此协议包含在自定义单元格和表格视图控制器中。 And make sure table view controller confirms to this protocol.并确保表视图控制器确认此协议。

In custom cell create two properties :在自定义单元格中创建两个属性:

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

In UIButton IBAction delegate click : ( Same can be done for any action in custom cell class which needs to be delegated back to view controller )UIButton IBAction 委托中单击:(对于需要委托回视图控制器的自定义单元格类中的任何操作都可以这样做

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

In table view controller cellForRowAtIndexPath after dequeing the cell, set the above properties.在单元格出列后,在表视图控制器cellForRowAtIndexPath ,设置上述属性。

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

And implement the delegate in table view controller:并在表视图控制器中实现委托:

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

This would be the ideal approach to get custom cell button actions in table view controller.这将是在表格视图控制器中获取自定义单元格按钮操作的理想方法。

Instead of playing with tags, I took different approach.我没有使用标签,而是采用了不同的方法。 Made delegate for my subclass of UITableViewCell(OptionButtonsCell) and added an indexPath var.为我的 UITableViewCell(OptionButtonsCell) 子类做了委托,并添加了一个 indexPath 变量。 From my button in storyboard I connected @IBAction to the OptionButtonsCell and there I send delegate method with the right indexPath to anyone interested.从故事板中的按钮,我将@IBAction 连接到 OptionButtonsCell,然后我将带有正确 indexPath 的委托方法发送给任何感兴趣的人。 In cell for index path I set current indexPath and it works :)在索引路径的单元格中,我设置了当前的 indexPath 并且它有效:)

Let the code speak for itself:让代码不言自明:

Swift 3 Xcode 8斯威夫特 3 Xcode 8

OptionButtonsTableViewCell.swift 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 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)")
}

This should help :-这应该有帮助:-

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

Here sender is the UIButton instance that is sending the event.这里的sender是发送事件的 UIButton 实例。 myTableView is the UITableView instance you're dealing with. myTableView是您正在处理的 UITableView 实例。

Just get the cell reference right and all the work is done.只需正确获取单元格引用即可完成所有工作。

You may need to remove the buttons from cell's contentView & add them directly to UITableViewCell instance as it's subview.您可能需要从单元格的 contentView 中删除按钮并将它们直接添加到 UITableViewCell 实例,因为它是子视图。

Or要么

You can formulate a tag naming scheme for different UIButtons in cell.contentView.您可以在 cell.contentView 中为不同的 UIButton 制定标签命名方案。 Using this tag, later you can know the row & section information as needed.使用这个标签,以后可以根据需要知道行&节信息。

Following code might Help you.以下代码可能对您有所帮助。

I have taken UITableView with custom prototype cell class named UITableViewCell inside UIViewController .我已经采取UITableView与命名的自定义原型细胞类UITableViewCellUIViewController

So i have ViewController.h , ViewController.m and TableViewCell.h , TableViewCell.m所以我有ViewController.hViewController.mTableViewCell.hTableViewCell.m

Here is the code for that:这是代码:

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

Custom cell class :自定义单元类:

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

and

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

Note : Here I have taken all UIControls using Storyboard.注意:这里我使用 Storyboard 获取了所有UIControls

Hope that can help you...!!!希望能帮到你。。。!!!

The reason i like below technique because it also help me to identify the section of table.我喜欢下面的技术的原因是因为它也帮助我识别表格的部分。

Add Button in cell cellForRowAtIndexPath:在单元格 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];

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

Hopes this help.希望这有帮助。

Use Swift closures :使用 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
    }
}

Tarun's code doesnt work on iOS7, since the UITableViewCell structure changed and now he would get "UITableViewCellScrollView" instead. Tarun 的代码在 iOS7 上不起作用,因为 UITableViewCell 结构发生了变化,现在他会得到“UITableViewCellScrollView”。

This post Getting UITableViewCell with superview in iOS 7 has a good solution creating a loop to find the correct parent view, regardless of any future changes in the structure.这篇文章在 iOS 7 中使用 superview 获取 UITableViewCell有一个很好的解决方案,即创建一个循环来找到正确的父视图,而不管未来的结构发生任何变化。 It boils down to creating a loop:它归结为创建一个循环:

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

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

The link has code for a more reusable solution, but this should work.该链接具有更可重用解决方案的代码,但这应该有效。

Swift 2.2斯威夫特 2.2

You need to add target for that button.您需要为该按钮添加目标。

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

FunctionName: connected // for example函数名称:已连接// 例如

And of course you need to set tag of that button since you are using it.当然,您需要设置该按钮的标签,因为您正在使用它。

myButton.tag = indexPath.row

You can achieve this by subclassing UITableViewCell.您可以通过继承 UITableViewCell 来实现这一点。 Use it in interface builder, drop a button on that cell, connect it via outlet and there you go.在界面生成器中使用它,在该单元格上放一个按钮,通过插座连接它,然后就可以了。

To get the tag in the connected function:要在连接的函数中获取标签:

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

Swift 3 with a Closure带有闭包的 Swift 3

A nice solution is using a closure in a custom UITableViewCell to callback to the viewController for an action.一个不错的解决方案是在自定义 UITableViewCell 中使用闭包来回调 viewController 以进行操作。

In cell:在单元格中:

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

In View Controller: Tableview Delegate在视图控制器中: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 
    }
}

I find it simplest to subclass the button inside your cell (Swift 3):我发现在您的单元格内对按钮进行子类化是最简单的(Swift 3):

class MyCellInfoButton: UIButton {
    var indexPath: IndexPath?
}

In your cell class:在您的单元格类中:

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

In the table view's or collection view's data source, when dequeueing the cell, give the button its index path:在表视图或集合视图的数据源中,当单元格出列时,为按钮指定其索引路径:

cell.infoButton.indexPath = indexPath

So you can just put these code into your table view controller:所以你可以把这些代码放到你的表视图控制器中:

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

And don't forget to set the button's class in your Interface Builder and link it to the handleTapOnCellInfoButton function!并且不要忘记在您的界面生成器中设置按钮的类并将其链接到handleTapOnCellInfoButton函数!


edited:编辑:

Using dependency injection.使用依赖注入。 To set up calling a closure:要设置调用闭包:

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

and inject the closure in the willDisplay method of the collection view's delegate:并在集合视图委托的 willDisplay 方法中注入闭包:

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

Its Work For me.它为我工作。

 - (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];
}

If you want to pass parameter value from cell to UIViewController using closure then如果您想使用闭包将参数值从单元格传递到 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
    }
}
// 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




}

for swift 4:快速 4:

 inside the cellForItemAt , cell.chekbx.addTarget(self, action: #selector(methodname), for: .touchUpInside) then outside of cellForItemAt @objc func methodname() { //your function code }

@Mani answer is good, however tags of views inside cell's contentView often are used for other purposes. @Mani 的回答很好,但是单元格的 contentView 中的视图标签通常用于其他目的。 You can use cell's tag instead (or cell's contentView tag):您可以使用单元格的标签(或单元格的 contentView 标签):

1) In your cellForRowAtIndexPath: method, assign cell's tag as index: 1) 在cellForRowAtIndexPath:方法中,将单元格的标签指定为索引:

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

2) Add target and action for your button as below: 2)为您的按钮添加目标和操作,如下所示:

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

3) Create method that returns row of the sender (thanks @Stenio Ferreira): 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) Code actions based on index: 4)基于索引的代码动作:

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

CustomTableCell.h is a UITableViewCell: CustomTableCell.h 是一个 UITableViewCell:

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

MyVC.m after imports:导入后的 MyVC.m:

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

Inside "cellForRowAtIndexPath" in MyVC.m:在 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:我的VC.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];
}
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];

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM