web-dev-qa-db-fra.com

Obtenir un clic sur un bouton dans UITableViewCell

J'ai un contrôleur de vue avec une vue de table et une pointe distincte pour le modèle de cellule de table. Le modèle de cellule a des boutons. Je souhaite accéder au clic sur le bouton en même temps que l'index de la cellule sélectionnée dans le contrôleur de vue où j'ai défini la vue Table.

Donc, j'ai ViewController.h et ViewController.m où j'ai les UITableView et TableTemplate.h, TableTemplate.m et TableTemplate.xib où j'ai le nib défini. Je veux l’événement clic sur le bouton avec l’index de la cellule dans ViewController.m.

Toute aide sur comment puis-je faire cela?

122
ankit_rck

1) Dans votre méthode cellForRowAtIndexPath:, attribuez la balise button en tant qu’index:

cell.yourbutton.tag = indexPath.row;

2) Ajouter cible et action pour votre bouton comme ci-dessous:

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

3) Code des actions basées sur l’index comme ci-dessous dans ViewControler:

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

Mises à jour pour plusieurs sections:

Vous pouvez vérifier ce lien pour détecter un clic de souris en mode tableau sur plusieurs lignes et sections.

245
Mani

Les délégués sont la voie à suivre.

Comme on le voit avec d'autres réponses, l'utilisation de vues peut devenir obsolète. Qui sait demain, il pourrait y avoir un autre wrapper et devra peut-être utiliser cell superview]superview]superview]superview]. Et si vous utilisez des balises, vous obtiendrez n nombre de conditions if else pour identifier la cellule. Pour éviter tout cela, installez des délégués. (Ce faisant, vous allez créer une classe de cellules réutilisable. Vous pouvez utiliser la même classe de cellules en tant que classe de base et tout ce que vous avez à faire est d'implémenter les méthodes de délégation.)

Nous avons d’abord besoin d’une interface (protocole) qui sera utilisée par la cellule pour communiquer (cliquer) sur les clics de bouton. (Vous pouvez créer un fichier .h distinct pour le protocole et l'inclure à la fois dans le contrôleur d'affichage de table et dans les classes de cellules personnalisées OR. Ajoutez-le simplement dans une classe de cellule personnalisée qui sera de toute façon incluse dans le contrôleur d'affichage de table)

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

Incluez ce protocole dans le contrôleur de vue de cellule et de table personnalisé. Et assurez-vous que le contrôleur de vue de table confirme ce protocole. 

Dans une cellule personnalisée, créez deux propriétés:

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

Dans UIButton délégué IBAction, cliquez sur: (La même chose peut être faite pour toute action de la classe de cellules personnalisée devant être déléguée pour visualiser le contrôleur).

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

Dans le contrôleur de vue tabulaire cellForRowAtIndexPath après la réduction de la cellule, définissez les propriétés ci-dessus.

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

Et implémentez le délégué dans le contrôleur de vue de table:

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

Ce serait l'approche idéale pour obtenir des actions de bouton de cellule personnalisées dans le contrôleur d'affichage de table.

143
GoodSp33d

Au lieu de jouer avec les tags, j'ai adopté une approche différente. Fait délégué pour ma sous-classe de UITableViewCell (OptionButtonsCell) et ajouté un indexPath var. À partir de mon bouton dans le storyboard, j'ai connecté @IBAction à OptionButtonsCell et j'envoie la méthode de délégation avec le bon indexPath à toute personne intéressée. Dans la cellule pour le chemin d'index, j'ai défini indexPath actuel et cela fonctionne :)

Laissez le code parler pour lui-même:

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)")
}
48
repoguy

Cela devrait aider: -

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

Ici expéditeur est l'instance UIButton qui envoie l'événement . myTableView est l'instance UITableView à laquelle vous faites face.

Obtenez juste la référence de cellule correcte et tout le travail est fait.

Vous devrez peut-être supprimer les boutons du contenu de la cellule contentView & Les ajouter directement à l'instance UITableViewCell en tant que sous-vue.

Ou 

Vous pouvez formuler un schéma de nommage de balise pour différents boutons UIB dans cell.contentView . À l'aide de cette balise, vous pourrez plus tard connaître les informations de ligne et de section selon vos besoins.

29
Tarun

Le code suivant peut vous aider.

J'ai pris UITableView avec la classe de cellules prototype personnalisée nommée UITableViewCell à l'intérieur de UIViewController.

J'ai donc ViewController.h, ViewController.m et TableViewCell.h, TableViewCell.m

Voici le code pour cela:

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

Classe de cellule personnalisée:

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

et 

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 : Ici, j'ai pris toutes les UIControls à l'aide de Storyboard.

J'espère que cela peut vous aider ... !!! 

20
Piyush Patel

La raison pour laquelle j’aime la technique ci-dessous car elle m’aide également à identifier la section du tableau.

Ajouter un bouton dans la cellule 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;
    }
}

Espère que cette aide.

12
Yogesh Lolusare

Le code de Tarun ne fonctionne pas sur iOS7, car la structure de UITableViewCell a changé et il obtiendrait maintenant "UITableViewCellScrollView".

Ce post Obtenir UITableViewCell avec superview dans iOS 7 présente une bonne solution en créant une boucle pour trouver la vue parente correcte, quelles que soient les modifications futures de la structure. Cela revient à créer une boucle:

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

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

Le lien contient du code pour une solution plus réutilisable, mais cela devrait fonctionner.

6
Stenio Ferreira

Swift 2.2

Vous devez ajouter une cible pour ce bouton.

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

NomFonction: connecté // par exemple

Et bien sûr, vous devez définir la balise de ce bouton puisque vous l'utilisez.

myButton.tag = indexPath.row

Vous pouvez y parvenir en sous-classant UITableViewCell. Utilisez-le dans le constructeur d’interface, déposez un bouton sur cette cellule, connectez-le via la sortie et voilà.

Pour obtenir le tag dans la fonction connectée:

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

Swift 3 avec une fermeture

Une solution intéressante consiste à utiliser une fermeture dans une UITableViewCell personnalisée pour rappeler le viewController à une action.

En cellule:

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

Dans le contrôleur de vue: délégué 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
Sevy11

Je trouve plus simple de sous-classer le bouton dans votre cellule (Swift 3):

class MyCellInfoButton: UIButton {
    var indexPath: IndexPath?
}

Dans ta classe de cellule:

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

Dans la source de données de la vue de table ou de la vue de collection, lors de la mise en file d'attente de la cellule, indiquez le chemin d'index du bouton:

cell.infoButton.indexPath = indexPath

Donc, vous pouvez simplement mettre ces codes dans votre contrôleur de vue de table:

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

Et n'oubliez pas de définir la classe du bouton dans votre Interface Builder et de la lier à la fonction handleTapOnCellInfoButton!


édité:

Utilisation de l'injection de dépendance. Pour paramétrer une fermeture:

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

et injectez la fermeture dans la méthode willDisplay du délégué de la vue de collection:

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

Son travail pour moi.

 - (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];
}
4
Yogesh Tarsariya
// 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
Hitesh Chauhan

Utilisez des fermetures rapides:

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
    }
}
1
valexa

pour Swift 4:

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

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

0
Radhe Yadav

La réponse @Mani est bonne, mais les balises de vues dans le contenu de la cellule sont souvent utilisées à d'autres fins. Vous pouvez utiliser la balise de cellule à la place (ou la balise contentView de la cellule):

1) Dans votre méthode cellForRowAtIndexPath:, attribuez l'indicateur de la cellule à l'index:

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

2) Ajouter cible et action pour votre bouton comme ci-dessous:

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

3) Créez une méthode qui renvoie la ligne de l'expéditeur (merci @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) Actions de code basées sur l'index:

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

CustomTableCell.h est un UITableViewCell:

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

MyVC.m après les importations:

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

Dans "cellForRowAtIndexPath" dans MyVC.m:

//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];
}
0
Mer