Question

J'ai un UITableView avec 5 UITableViewCells . Chaque cellule contient un UIButton qui est configuré comme suit:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     NSString *identifier = @"identifier";
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
         [cell autorelelase];

         UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
         [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
         [button setTag:1];
         [cell.contentView addSubview:button];

         [button release];
     }

     UIButton *button = (UIButton *)[cell viewWithTag:1];
     [button setTitle:@"Edit" forState:UIControlStateNormal];

     return cell;
}

Ma question est la suivante: dans la méthode buttonPressedAction: , comment savoir quel bouton a été enfoncé. J'ai envisagé d'utiliser des tags mais je ne suis pas sûr que ce soit le meilleur itinéraire. J'aimerais pouvoir en quelque sorte marquer indexPath sur le contrôle.

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    // how do I know which button sent this message?
    // processing button press for this row requires an indexPath. 
}

Quelle est la méthode standard pour ce faire?

Modifier:

Je l'ai un peu résolu en procédant comme suit. Je voudrais quand même avoir un avis, que ce soit la manière habituelle de le faire ou s’il existe une meilleure façon?

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     NSString *identifier = @"identifier";
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
         [cell autorelelase];

         UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
         [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
         [cell.contentView addSubview:button];

         [button release];
     }

     UIButton *button = (UIButton *)[cell.contentView.subviews objectAtIndex:0];
     [button setTag:indexPath.row];
     [button setTitle:@"Edit" forState:UIControlStateNormal];

     return cell;
}

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    int row = button.tag;
}

Ce qui est important à noter, c’est que je ne peux pas définir l’étiquette dans la création de la cellule car celle-ci risque d’être retirée de la file d’attente. C'est très sale. Il doit y avoir un meilleur moyen.

Était-ce utile?

La solution

Dans notre exemple accessoire, la méthode suivante est utilisée:

[button addTarget:self action:@selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside];

La coordonnée tactile du gestionnaire tactile est alors extraite et le chemin d'index est calculé à partir de cette coordonnée:

- (void)checkButtonTapped:(id)sender
{
    CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    if (indexPath != nil)
    {
     ...
    }
}

Autres conseils

J'ai trouvé que la méthode consistant à utiliser Superview de Superview pour obtenir une référence à IndexPath de la cellule fonctionnait parfaitement. Merci à iphonedevbook.com (macnsmith) pour le conseil texte du lien .

-(void)buttonPressed:(id)sender {
 UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
 NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
...

}

Voici comment je le fais. Simple et concis:

- (IBAction)buttonTappedAction:(id)sender
{
    CGPoint buttonPosition = [sender convertPoint:CGPointZero
                                           toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    ...
}

Nous avons trouvé une bonne solution à ce problème ailleurs, sans déconner avec les balises sur le bouton:

- (void)buttonPressedAction:(id)sender {

    NSSet *touches = [event allTouches];
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];

    // do stuff with the indexPath...
}

Pourquoi ne pas envoyer des informations telles que NSIndexPath dans UIButton à l'aide d'une injection d'exécution.

1) Vous avez besoin du runtime lors de l'importation

2) ajouter une constante statique

3) Ajoutez NSIndexPath à votre bouton lors de l'exécution à l'aide de:

(void) setMetaData: (id) cible avecObject: (id) newObj

4) lors de l’appui sur le bouton, obtenez les métadonnées en utilisant:

(id) metaData: (id) cible

Profitez

    #import <objc/runtime.h>
    static char const * const kMetaDic = "kMetaDic";


    #pragma mark - Getters / Setters

- (id)metaData:(id)target {
    return objc_getAssociatedObject(target, kMetaDic);
}

- (void)setMetaData:(id)target withObject:(id)newObj {
    objc_setAssociatedObject(target, kMetaDic, newObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}



    #On the cell constructor
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    ....
    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    ....
    [btnSocial addTarget:self
                                   action:@selector(openComments:)
                         forControlEvents:UIControlEventTouchUpInside];

    #add the indexpath here or another object
    [self setMetaData:btnSocial withObject:indexPath];

    ....
    }



    #The action after button been press:

    - (IBAction)openComments:(UIButton*)sender{

        NSIndexPath *indexPath = [self metaData:sender];
        NSLog(@"indexPath: %d", indexPath.row);

        //Reuse your indexpath Now
    }

La réponse (@Vladimir) est Swift:

var buttonPosition = sender.convertPoint(CGPointZero, toView: self.tableView)
var indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)!

Bien que la recherche de indexPath! = nil me donne le doigt ... "NSIndexPath n'est pas un sous-type de NSString"

.
func buttonAction(sender:UIButton!)
    {
        var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tablevw)
        let indexPath = self.tablevw.indexPathForRowAtPoint(position)
        let cell: TableViewCell = tablevw.cellForRowAtIndexPath(indexPath!) as TableViewCell
        println(indexPath?.row)
        println("Button tapped")
    }

J'utiliserais la propriété de balise comme vous l'avez dit, en définissant la balise comme suit:

[button setTag:indexPath.row];

puis obtenir la balise à l'intérieur du boutonPressedAction comme suit:

((UIButton *)sender).tag

Ou

UIButton *button = (UIButton *)sender; 
button.tag;

Bien que j'aime bien les tags ... si vous ne voulez pas utiliser les tags pour une raison quelconque, vous pouvez créer un membre NSArray de boutons prédéfinis:

NSArray* buttons ;

puis créez ces boutons avant de restituer la tableView et insérez-les dans le tableau.

Ensuite, à l'intérieur de la fonction tableView: cellForRowAtIndexPath: , vous pouvez effectuer:

UIButton* button = [buttons objectAtIndex:[indexPath row] ] ;
[cell.contentView addSubview:button];

Puis, dans la fonction buttonPressedAction: , vous pouvez faire

- (void)buttonPressedAction:(id)sender {
   UIButton* button = (UIButton*)sender ;
   int row = [buttons indexOfObject:button] ;
   // Do magic
}

POUR GÉRER LES SECTIONS - J'ai stocké le NSIndexPath dans un UITableViewCell personnalisé

IN CLKIndexPricesHEADERTableViewCell.xib

IN IB Ajouter UIButton à XIB - NE PAS ajouter d’action!

Ajouter un point d’exploitation @ propriété (conserver, non atomique). IBOutlet UIButton * buttonIndexSectionClose;

NE PAS CTRL + DRAGER une action dans IB (fait dans le code ci-dessous)

@interface CLKIndexPricesHEADERTableViewCell : UITableViewCell
...
@property (retain, nonatomic) IBOutlet UIButton *buttonIndexSectionClose;
@property (nonatomic, retain) NSIndexPath * indexPathForCell;
@end

Dans viewForHeaderInSection (devrait également fonctionner pour cellForRow .... etc si votre table ne comporte qu'une seule section)

- viewForHeaderInSection is called for each section 1...2...3
- get the cell CLKIndexPricesHEADERTableViewCell 
- getTableRowHEADER just does the normal dequeueReusableCellWithIdentifier
- STORE the indexPath IN the UITableView cell
- indexPath.section = (NSInteger)section
- indexPath.row = 0 always (we are only interested in sections)

- (UIView *) tableView:(UITableView *)tableView1 viewForHeaderInSection:(NSInteger)section {


    //Standard method for getting a UITableViewCell
    CLKIndexPricesHEADERTableViewCell * cellHEADER = [self getTableRowHEADER];

... utilisez la section pour obtenir des données pour votre cellule

... remplissez-le

   indexName        = ffaIndex.routeCode;
   indexPrice       = ffaIndex.indexValue;

   //

   [cellHEADER.buttonIndexSectionClose addTarget:self
                                          action:@selector(buttonDELETEINDEXPressedAction:forEvent:)
                                forControlEvents:UIControlEventTouchUpInside];


   cellHEADER.indexPathForCell = [NSIndexPath indexPathForRow:0 inSection:section];


    return cellHEADER;
}

USER appuie sur le bouton DELETE d'un en-tête de section, ce qui appelle

- (void)buttonDELETEINDEXPressedAction:(id)sender forEvent:(UIEvent *)event
{
    NSLog(@"%s", __PRETTY_FUNCTION__);


    UIView *  parent1 = [sender superview];   // UiTableViewCellContentView
    //UIView *myContentView = (UIView *)parent1;

    UIView *  parent2 = [parent1 superview];  // custom cell containing the content view
    //UIView *  parent3 = [parent2 superview];  // UITableView containing the cell
    //UIView *  parent4 = [parent3 superview];  // UIView containing the table


    if([parent2 isMemberOfClass:[CLKIndexPricesHEADERTableViewCell class]]){
        CLKIndexPricesHEADERTableViewCell *myTableCell = (CLKIndexPricesHEADERTableViewCell *)parent2;

        //UITableView *myTable = (UITableView *)parent3;
        //UIView *mainView = (UIView *)parent4;

        NSLog(@"%s indexPath.section,row[%d,%d]", __PRETTY_FUNCTION__, myTableCell.indexPathForCell.section,myTableCell.indexPathForCell.row);

        NSString *key = [self.sortedKeysArray objectAtIndex:myTableCell.indexPathForCell.section];
        if(key){
            NSLog(@"%s DELETE object at key:%@", __PRETTY_FUNCTION__,key);
            self.keyForSectionIndexToDelete = key;
            self.sectionIndexToDelete = myTableCell.indexPathForCell.section;

            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Remove Index"
                                                                message:@"Are you sure"
                                                               delegate:self
                                                      cancelButtonTitle:@"No"
                                                      otherButtonTitles:@"Yes", nil];
            alertView.tag = kALERTVIEW_REMOVE_ONE_INDEX;
            [alertView show];
            [alertView release];
            //------
        }else{
            NSLog(@"ERROR: [%s] key is nil for section:%d", __PRETTY_FUNCTION__,myTableCell.indexPathForCell.section);
        }

    }else{
        NSLog(@"ERROR: [%s] CLKIndexPricesHEADERTableViewCell not found", __PRETTY_FUNCTION__);
    }
}

Dans cet exemple, j'ai ajouté un bouton Supprimer, ce qui devrait afficher UIAlertView pour le confirmer.

Je stocke la section et la clé dans le dictionnaire, stockant des informations sur la section dans un ivar du VC

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
   if(alertView.tag == kALERTVIEW_REMOVE_ONE_INDEX){
        if(buttonIndex==0){
            //NO
            NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
            //do nothing
        }
        else if(buttonIndex==1){
            //YES
            NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
            if(self.keyForSectionIndexToDelete != nil){

                //Remove the section by key
                [self.indexPricesDictionary removeObjectForKey:self.keyForSectionIndexToDelete];

                //sort the keys so sections appear alphabetically/numbericsearch (minus the one we just removed)
                [self updateTheSortedKeysArray];                

                //Delete the section from the table using animation
                [self.tableView beginUpdates];

                [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:self.sectionIndexToDelete]
                              withRowAnimation:UITableViewRowAnimationAutomatic];
                [self.tableView endUpdates];

                //required to trigger refresh of myTableCell.indexPathForCell else old values in UITableViewCells
                [self.tableView reloadData];
            }else{
                NSLog(@"ERROR: [%s] OBJECT is nil", __PRETTY_FUNCTION__);
            }
        }
        else {
            NSLog(@"ERROR: [%s] UNHANDLED BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
        }
    }else {
        NSLog(@"ERROR: [%s] unhandled ALERTVIEW TAG:%d", __PRETTY_FUNCTION__,alertView.tag);
    }
}
A better way would be to subclass your button and add a indexPath property to it.

//Implement a subclass for UIButton.

@interface NewButton:UIButton
@property(nonatomic, strong) NSIndexPath *indexPath;


Make your button of type NewButton in the XIB or in the code whereever you are initializing them.

Then in the cellForRowAtIndexPath put the following line of code.

button.indexPath = indexPath;

return cell; //As usual



Now in your IBAction

-(IBAction)buttonClicked:(id)sender{
   NewButton *button = (NewButton *)sender;

//Now access the indexPath by buttons property..

   NSIndexPath *indexPath = button.indexPath; //:)
}

Avec Swift 4.2 et iOS 12, vous pouvez choisir l’un des 5 exemples complets suivants afin de résoudre votre problème.

# 1. Utilisation du de UIView pour convertir (_: to:) et de UITableView pour indexPathForRow (at:) h2>
import UIKit

private class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        cell.button.addTarget(self, action: #selector(customCellButtonTapped), for: .touchUpInside)
        return cell
    }

    @objc func customCellButtonTapped(_ sender: UIButton) {
        let point = sender.convert(CGPoint.zero, to: tableView)
        guard let indexPath = tableView.indexPathForRow(at: point) else { return }
        print(indexPath)
    }

}

# 2. Utilisation du de UIView pour convertir (_: to:) et de UITableView pour indexPathForRow (at:) (alternatif )

Ceci est une alternative à l'exemple précédent dans lequel nous passons nil au paramètre target dans addTarget (_: action: pour:) . Ainsi, si le premier intervenant n’implémente pas l’action, elle sera envoyée au prochain intervenant de la chaîne de réponse jusqu’à ce qu’une mise en œuvre appropriée soit trouvée.

import UIKit

private class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        button.addTarget(nil, action: #selector(TableViewController.customCellButtonTapped), for: .touchUpInside)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        return cell
    }

    @objc func customCellButtonTapped(_ sender: UIButton) {
        let point = sender.convert(CGPoint.zero, to: tableView)
        guard let indexPath = tableView.indexPathForRow(at: point) else { return }
        print(indexPath)
    }

}

# 3. Utilisation du indexPath (for:) de UITableView et du motif de délégation

Dans cet exemple, nous définissons le contrôleur de vue en tant que délégué de la cellule. Lorsque vous appuyez sur le bouton de la cellule, il déclenche un appel à la méthode appropriée du délégué.

import UIKit

protocol CustomCellDelegate: AnyObject {
    func customCellButtonTapped(_ customCell: CustomCell)
}

class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)
    weak var delegate: CustomCellDelegate?

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @objc func buttonTapped(sender: UIButton) {
        delegate?.customCellButtonTapped(self)
    }

}
import UIKit

class TableViewController: UITableViewController, CustomCellDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

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

    // MARK: - CustomCellDelegate

    func customCellButtonTapped(_ customCell: CustomCell) {
        guard let indexPath = tableView.indexPath(for: customCell) else { return }
        print(indexPath)
    }

}

# 4. Utilisation du indexPath (for:) de UITableView et d'une fermeture pour la délégation

Ceci est une alternative à l'exemple précédent dans lequel nous utilisons une fermeture au lieu d'une déclaration de délégué de protocole pour gérer le clic du bouton.

import UIKit

class CustomCell: UITableViewCell {

    let button = UIButton(type: .system)
    var buttontappedClosure: ((CustomCell) -> Void)?

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        button.setTitle("Tap", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        contentView.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
        button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @objc func buttonTapped(sender: UIButton) {
        buttontappedClosure?(self)
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        cell.buttontappedClosure = { [weak tableView] cell in
            guard let indexPath = tableView?.indexPath(for: cell) else { return }
            print(indexPath)
        }
        return cell
    }

}

# 5. Utilisation du tableType de UITableViewCell et du tableView UITableViewDelegate (_: accessoireButtonTappedForRowWith:)

Si votre bouton est un contrôle accessoire standard de UITableViewCell , tout appui sur celui-ci déclenchera un appel au tableView de UITableViewDelegate (_: accessoireButtonTappedForRowWith: ) , vous permettant d’obtenir le chemin d’index associé.

import UIKit

private class CustomCell: UITableViewCell {

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        accessoryType = .detailButton
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}
import UIKit

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        return cell
    }

    override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
        print(indexPath)
    }

}

Cela fonctionne aussi pour moi, merci @Cocoanut

J'ai trouvé que la méthode consistant à utiliser Superview de Superview pour obtenir une référence à IndexPath de la cellule fonctionnait parfaitement. Merci à iphonedevbook.com (macnsmith) pour le texte du lien vers la pointe

-(void)buttonPressed:(id)sender {
 UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
 NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
...

}

vous pouvez utiliser le modèle de balise:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
     NSString *identifier = @"identifier";
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
     if (cell == nil) {
         cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
         [cell autorelelase];

         UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
         [button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
         [button setTag:[indexPath row]]; //use the row as the current tag
         [cell.contentView addSubview:button];

         [button release];
     }

     UIButton *button = (UIButton *)[cell viewWithTag:[indexPath row]]; //use [indexPath row]
     [button setTitle:@"Edit" forState:UIControlStateNormal];

     return cell;
}

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    //button.tag has the row number (you can convert it to indexPath)
}

Est-ce que je manque quelque chose? Vous ne pouvez pas simplement utiliser l'expéditeur pour identifier le bouton. L'expéditeur vous donnera des informations comme ceci:

<UIButton: 0x4b95c10; frame = (246 26; 30 30); opaque = NO; tag = 104; layer = <CALayer: 0x4b95be0>>

Ensuite, si vous souhaitez modifier les propriétés du bouton, dites à l’arrière-plan que vous indiquez à l'expéditeur:

[sender setBackgroundImage:[UIImage imageNamed:@"new-image.png"] forState:UIControlStateNormal];

Si vous avez besoin de la balise, la méthode d'ACBurk est correcte.

// how do I know which button sent this message?
// processing button press for this row requires an indexPath.

En fait, c'est assez simple:

- (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;
    CGPoint rowButtonCenterInTableView = [[rowButton superview] convertPoint:rowButton.center toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:rowButtonCenterInTableView];
    MyTableViewItem *rowItem = [self.itemsArray objectAtIndex:indexPath.row];
    // Now you're good to go.. do what the intention of the button is, but with
    // the context of the "row item" that the button belongs to
    [self performFooWithItem:rowItem];
}

Travaille bien pour moi: P

si vous souhaitez ajuster la configuration de votre action-cible, vous pouvez inclure le paramètre event dans la méthode, puis utiliser les touches de cet événement pour résoudre les coordonnées de cette dernière. Les coordonnées doivent encore être résolues dans les limites de la vue tactile, mais cela peut sembler plus facile pour certaines personnes.

créez un tableau nsmutable et placez tous les boutons de ce tableau à l'aide de [array addObject: yourButton];

dans la méthode de pression de bouton

-

 (void)buttonPressedAction:(id)sender
{
    UIButton *button = (UIButton *)sender;

for(int i=0;i<[yourArray count];i++){

if([buton isEqual:[yourArray objectAtIndex:i]]){

//here write wat u need to do

}
}

Une légère variation de la réponse de Cocoanuts (qui m'a aidé à résoudre ce problème) lorsque le bouton était dans le pied de page d'un tableau (ce qui vous empêche de rechercher la "cellule sur laquelle vous avez cliqué":

-(IBAction) buttonAction:(id)sender;
{
    id parent1 = [sender superview];   // UiTableViewCellContentView
    id parent2 = [parent1 superview];  // custom cell containing the content view
    id parent3 = [parent2 superview];  // UITableView containing the cell
    id parent4 = [parent3 superview];  // UIView containing the table

    UIView *myContentView = (UIView *)parent1;
    UITableViewCell *myTableCell = (UITableViewCell *)parent2;
    UITableView *myTable = (UITableView *)parent3;
    UIView *mainView = (UIView *)parent4;

    CGRect footerViewRect = myTableCell.frame;
    CGRect rect3 = [myTable convertRect:footerViewRect toView:mainView];    

    [cc doSomethingOnScreenAtY:rect3.origin.y];
}

J'utilise toujours des tags.

Vous devez sous-classer le UITableviewCell et gérer la pression du bouton à partir de là.

C'est simple. faire une cellule personnalisée et prendre une sortie de bouton

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
         NSString *identifier = @"identifier";
        customCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

    cell.yourButton.tag = indexPath.Row;

- (void)buttonPressedAction:(id)sender

changez l'identifiant dans la méthode ci-dessus en (UIButton *)

Vous pouvez obtenir la valeur correspondant au bouton sélectionné en faisant sender.tag.

Sous-classe le bouton pour stocker la valeur requise, peut-être créer un protocole (ControlWithData ou autre). Définissez la valeur lorsque vous ajoutez le bouton à la cellule de la vue tableau. Dans votre événement de retouche, voyez si l'expéditeur respecte le protocole et extrayez les données. Je stocke normalement une référence à l'objet réel qui est rendu dans la cellule de la vue tableau.

MISE À JOUR SWIFT 2

Voici comment savoir quel bouton a été utilisé + envoyer des données à un autre ViewController à partir de indexPath.row de ce bouton, car je suppose que c'est le but pour la plupart!

@IBAction func yourButton(sender: AnyObject) {


     var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
        let indexPath = self.tableView.indexPathForRowAtPoint(position)
        let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
        UITableViewCell
        print(indexPath?.row)
        print("Tap tap tap tap")

    }

Pour ceux qui utilisent une classe ViewController et ont ajouté un tableView, j'utilise un ViewController au lieu d'un TableViewController, j'ai donc ajouté manuellement le tableView pour pouvoir y accéder.

Voici le code permettant de transmettre des données à un autre VC lorsque vous appuyez sur ce bouton et transmettez le code de la cellule indexPath.row

@IBAction func moreInfo(sender: AnyObject) {

    let yourOtherVC = self.storyboard!.instantiateViewControllerWithIdentifier("yourOtherVC") as! YourOtherVCVIewController



    var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
    let indexPath = self.tableView.indexPathForRowAtPoint(position)
    let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
    UITableViewCell
    print(indexPath?.row)
    print("Button tapped")


    yourOtherVC.yourVarName = [self.otherVCVariable[indexPath!.row]]

    self.presentViewController(yourNewVC, animated: true, completion: nil)

}

Remarquez que j'utilise une cellule personnalisée. Ce code fonctionne parfaitement pour moi.

 @IBAction func call(sender: UIButton)
    {
        var contentView = sender.superview;
        var cell = contentView?.superview as EmployeeListCustomCell
        if (!(cell.isKindOfClass(EmployeeListCustomCell)))
        {
            cell = (contentView?.superview)?.superview as EmployeeListCustomCell
        }

        let phone = cell.lblDescriptionText.text!
        //let phone = detailObject!.mobile!
        let url:NSURL = NSURL(string:"tel://"+phone)!;
        UIApplication.sharedApplication().openURL(url);
    }

La solution de Chris Schwerdt puis à Swift a fonctionné pour moi:

@IBAction func rateButtonTapped(sender: UIButton) {
    let buttonPosition : CGPoint = sender.convertPoint(CGPointZero, toView: self.ratingTableView)
    let indexPath : NSIndexPath = self.ratingTableView.indexPathForRowAtPoint(buttonPosition)!

    print(sender.tag)
    print(indexPath.row)
}

Ce problème comporte deux parties:

1) Obtention du chemin d'index de UITableViewCell qui contient UIButton pressé

Il y a des suggestions comme:

  • Mise à jour de la balise de UIButton dans cellForRowAtIndexPath: à l'aide de la valeur row de chemin d'index. Ce n'est pas une bonne solution car cela nécessite de mettre à jour la balise de manière continue et cela ne fonctionne pas avec les vues de table avec plus d'une section.

  • Ajout d'une propriété NSIndexPath à une cellule personnalisée et mise à jour à la place de la balise de UIButton dans cellForRowAtIndexPath: méthode. Ceci résout le problème de sections multiples mais n’est toujours pas satisfaisant car il nécessite toujours une mise à jour.

  • Conserver une référence faible au parent UITableView dans la cellule personnalisée lors de sa création et à l'aide de la méthode indexPathForCell: pour obtenir le chemin d'index. Cela semble un peu mieux, pas besoin de mettre à jour quoi que ce soit dans la méthode cellForRowAtIndexPath: , mais nécessite néanmoins de définir une référence faible lors de la création de la cellule personnalisée.

  • Utilisation de la propriété superView de la cellule pour obtenir une référence au parent UITableView . Il n'est pas nécessaire d'ajouter des propriétés à la cellule personnalisée ni de définir / mettre à jour quoi que ce soit lors de la création / ultérieure. Mais le superView de la cellule dépend des détails de la mise en œuvre iOS. Donc, il ne peut pas être utilisé directement.

Mais cela peut être réalisé en utilisant une simple boucle, car nous sommes sûrs que la cellule en question doit être dans une UITableView:

UIView* view = self;
while (view && ![view isKindOfClass:UITableView.class])
    view = view.superview;
UITableView* parentTableView = (UITableView*)view;

Ces suggestions peuvent donc être combinées dans une méthode de cellule personnalisée simple et sûre pour obtenir le chemin d'index:

- (NSIndexPath *)indexPath
{
    UIView* view = self;

    while (view && ![view isKindOfClass:UITableView.class])
        view = view.superview;

    return [(UITableView*)view indexPathForCell:self];
}

A partir de maintenant, cette méthode peut être utilisée pour détecter le UIButton sur lequel vous appuyez.

2) Information des autres parties sur l'événement de presse de bouton

Après avoir déterminé en interne quel UIButton est utilisé dans quelle cellule personnalisée avec le chemin d'index exact, ces informations doivent être envoyées aux autres parties (très probablement le contrôleur de vue gérant le UITableView ). Ainsi, cet événement de clic de bouton peut être géré dans une abstraction et un niveau logique similaires à la méthode de didSelectRowAtIndexPath: du délégué UITableView.

Deux approches peuvent être utilisées pour cela:

a) Délégation: une cellule personnalisée peut posséder une propriété delegate et définir un protocole. Lorsque le bouton est enfoncé, il applique simplement ses méthodes de délégation sur sa propriété delegate . Mais cette propriété delegate doit être définie pour chaque cellule personnalisée lors de leur création. Une cellule personnalisée peut également choisir d’exécuter ses méthodes de délégation sur le délégué de sa vue de table parent également.

b) Centre de notifications: les cellules personnalisées peuvent définir un nom de notification personnalisé et publier cette notification avec les informations de chemin d'index et de vue de table parent fournies dans l'objet userInfo . Nul besoin de définir quoi que ce soit pour chaque cellule, il suffit d’ajouter un observateur pour la notification de la cellule personnalisée.

J'utilise une solution qui sous-classe UIButton et j'ai pensé que je devrais le partager ici, codes dans Swift:

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

N'oubliez pas de mettre à jour son chemin indexPath dans cellForRow (at:)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let returnCell = tableView.dequeueReusableCell(withIdentifier: "cellWithButton", for: indexPath) as! cellWithButton
    ...
    returnCell.button.indexPath = IndexPath
    returnCell.button.addTarget(self, action:#selector(cellButtonPressed(_:)), for: .touchUpInside)

    return returnCell
}

Ainsi, lorsque vous répondez à l'événement du bouton, vous pouvez l'utiliser comme

.
func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top