UITableViewCell内のボタンのクリックを取得


140

私は、テーブルビューを備えたビューコントローラーと、テーブルセルテンプレート用の個別のペン先を持っています。セルテンプレートにはいくつかのボタンがあります。テーブルビューを定義したビューコントローラー内でクリックされたセルのインデックスと共にボタンクリックにアクセスしたいと思います。

持っている私はそうViewController.hViewController.m、私が持っているところUITableViewTableTemplate.hTableTemplate.mそしてTableTemplate.xibどこ私はペン先を定義しています。のセルインデックスを含むボタンクリックイベントが必要ですViewController.m

どうすればそれを行うことができますか?

回答:


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
これは、ステップ2のInterface Builder(IB)を介して行うこともできます。ボタンタグが設定されていることを確認してください。アクションの呼び出しを混同したくはありません。IBを使用するか、コードで明示的に実行します。
センテンシア2014

@ManiこれはMVCを壊しません-アクションはセルではなくTableView内にあります。
davecom 2014年

@davecom(IBを介して)ボタンターゲットをセルとして設定した場合、tableViewからトリガーされる方法は?または、ボタンターゲットをセルのxibに配置されているtableviewに接続する方法はありますか?
マニ2014年

24
このソリューションでは、行の挿入と削除を開始すると問題が発生します。行がシフトされても、タグは更新されません。行への参照を保持する代わりに。一意のオブジェクトIDへの参照を保持することをお勧めします。
Vincent Cheong

1
ビューのタグ属性に値を割り当てていることに気付いたときはいつでも、後で悪臭を放つ非常に悪いコード臭があります。最初に見つけたSO投稿ではなく、目標を達成するためのより良い方法を探してください。
TigerCoding

148

デリゲートは、進むべき道です。

他の回答で見たように、ビューを使用すると時代遅れになる可能性があります。明日は別のラッパーがあり、使用する必要があるかもしれませんcell superview]superview]superview]superview]。タグを使用すると、セルを特定するためのn個のif else条件が作成されます。そのすべてを回避するためにデリゲートを設定します。(これにより、再利用可能なセルクラスが作成されます。同じセルクラスを基本クラスとして使用でき、デリゲートメソッドを実装するだけです。)

最初に、セルが通信(委任)ボタンをクリックするために使用するインターフェース(プロトコル)が必要です。(プロトコル用に別の.hファイルを作成し、テーブルビューコントローラーとカスタムセルクラスの両方に含めるか、カスタムセルクラスに追加して、とにかくテーブルビューコントローラーに含めることができます

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

このプロトコルをカスタムセルおよびテーブルビューコントローラーに含めます。そして、Table View Controllerがこのプロトコルを確認することを確認してください。

カスタムセルで2つのプロパティを作成します。

@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 2015年

2
各セルでデリゲートを使用するとメモリが消費されるので、タグを使用すると友人から聞いたことがあります。これは本当ですか?
ビスタ2015

2
この質問の男のチェックstackoverflow.com/questions/31649220/...
Nischal羽田

@the_UBタグを設定してから単一の参照を保存するまでの間にそれほど多くのことはあり得ません。おそらく、タグはより多くのメモリを消費します。
Ian Warburton

66

タグをいじる代わりに、私は別のアプローチをとりました。UITableViewCell(OptionButtonsCell)のサブクラスのデリゲートを作成し、indexPath変数を追加しました。ストーリーボードのボタンから、@ IBActionをOptionButtonsCellに接続し、適切なindexPathを持つデリゲートメソッドを興味のある人に送信します。インデックスパスのセルで現在のindexPathを設定し、それが機能します:)

コードにそれ自体を語らせてください:

Swift 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 //エラー:プロトコルのUITableViewDataSource 'から「MyTableViewController」の冗長準拠
Ulug'bek Ro'zimboyev

UITableViewDataSourceに複数回準拠しようとしているようです。おそらく、すでにデータソースに準拠している拡張機能があるのでしょうか?コードなしではこれ以上のことはできません
Maciej Chrzastek

1
データを渡してセグエを実行し、別のビューコントローラーに移動する方法
Milad Faridnia

2
最良かつ最もクリーンなソリューション!
2018

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

2
これは非常に単純なセルに適しています。ただし、セルにビューの深いツリーがある場合、Maniの答えが最適です。
センテンシア2014

3
iOS 7では、UITableViewCell * cell =(UITableViewCell *)[[[sender superview] superview] superview];になります。ありがとうございました。
Rajan Maharjan 2014年

この質問の男のチェックstackoverflow.com/questions/31649220/...
Nischal羽田

22

次のコードはあなたを助けるかもしれません。

内部でUITableView名前が付けられたカスタムプロトタイプセルクラスで撮影しましたUITableViewCellUIViewController

だから私は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ストーリーボードを使用し。

お役に立てれば幸いです... !!!


これまでで最高の方法
Daniel

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

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

In View Controller:Tableview Delegate

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

そして、インターフェイスビルダーでボタンのクラスを設定し、それを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

Swift 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

@マニの答えは良いですが、セルのcontentView内のビューのタグは他の目的で使用されることがよくあります。代わりにセルのタグ(またはセルの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];

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.