Domanda

Ho un UITableView con 5 UITableViewCells . Ogni cella contiene un UIButton che è impostato come segue:

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

La mia domanda è questa: nel metodo buttonPressedAction: , come faccio a sapere quale pulsante è stato premuto. Ho preso in considerazione l'uso dei tag ma non sono sicuro che questo sia il percorso migliore. Vorrei poter in qualche modo taggare indexPath sul controllo.

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

Qual è il modo standard di farlo?

Modifica

L'ho risolto un po 'nel modo seguente. Mi piacerebbe ancora avere un'opinione se questo è il modo standard di farlo o esiste un modo migliore?

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

La cosa importante da notare è che non posso impostare il tag nella creazione della cella poiché la cella potrebbe invece essere rimossa. Sembra molto sporco. Deve esserci un modo migliore.

È stato utile?

Soluzione

Nell'esempio Accessory di Apple viene utilizzato il seguente metodo:

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

Quindi nel gestore di tocco la coordinata del tocco viene recuperata e il percorso dell'indice viene calcolato da quella coordinata:

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

Altri suggerimenti

Ho trovato il metodo di utilizzo della superview della superview per ottenere un riferimento all'indicePath della cella funzionava perfettamente. Grazie a iphonedevbook.com (macnsmith) per l'informazione testo del link

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

}

Ecco come lo faccio. Semplice e conciso:

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

Ho trovato una buona soluzione a questo problema altrove, senza problemi con i tag sul pulsante:

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

Che ne dici di inviare informazioni come NSIndexPath nel UIButton usando il runtime injection.

1) È necessario il runtime sull'importazione

2) aggiungi costante statica

3) aggiungi NSIndexPath al tuo pulsante in fase di esecuzione utilizzando:

(vuoto) setMetaData: (id) target withObject: (id) newObj

4) alla pressione del pulsante ottieni metadati usando:

(id) Metadati: (id) destinazione

Godetevi

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

Per fare la risposta di (@Vladimir) è Swift:

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

Anche se la ricerca di indexPath! = zero mi dà il dito ... " NSIndexPath non è un sottotipo di 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")
    }

Vorrei usare la proprietà tag come hai detto, impostando il tag in questo modo:

[button setTag:indexPath.row];

quindi ottenere il tag all'interno del pulsantePressedAction in questo modo:

((UIButton *)sender).tag

o

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

Anche se mi piace il modo dei tag ... se non vuoi usare i tag per qualsiasi motivo, potresti creare un membro NSArray di pulsanti premade:

NSArray* buttons ;

quindi crea quei pulsanti prima di eseguire il rendering di tableView e inseriscili nell'array.

Quindi all'interno della tabella tableView: cellForRowAtIndexPath: puoi fare:

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

Quindi nella funzione buttonPressedAction: , puoi farlo

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

PER MANEGGIARE LE SEZIONI - Ho archiviato NSIndexPath in un UITableViewCell personalizzato

IN CLKIndexPricesHEADERTableViewCell.xib

IN IB Aggiungi UIButton a XIB - NON aggiungere azione!

Aggiungi outlet @property (conservare, non anatomico) IBOutlet UIButton * buttonIndexSectionClose;

NON CTRL + DRAG un'azione in IB (eseguita nel codice seguente)

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

In viewForHeaderInSection (dovrebbe funzionare anche per cellForRow .... ecc se la tabella ha solo una sezione)

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

... usa la sezione per ottenere dati per la tua cella

... compila

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

L'UTENTE preme il pulsante CANC su un'intestazione di sezione e questo chiama

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

In questo esempio ho aggiunto un pulsante Elimina, quindi dovrei mostrare UIAlertView per confermarlo

Memorizzo la sezione e digito nel dizionario memorizzando le informazioni sulla sezione in un ivar nel 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; //:)
}

Con Swift 4.2 e iOS 12, puoi scegliere uno dei 5 seguenti esempi completi per risolvere il tuo problema.


# 1. Usando il convertito di UIView (_: in :) e UITableView indexPathForRow (at :)

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. Usando il convertito di UIView (_: in :) e UITableView indexPathForRow (at :) (alternativa )

Questa è un'alternativa all'esempio precedente in cui passiamo zero al parametro target in addTarget (_: action: for :) . In questo modo, se il primo risponditore non implementa l'azione, verrà inviato al risponditore successivo nella catena del risponditore fino a quando non verrà trovata una corretta implementazione.

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. Usando indexPath di UITableView (per :) e il modello delegato

In questo esempio, impostiamo il controller di visualizzazione come delegato della cella. Quando si tocca il pulsante della cella, viene attivata una chiamata al metodo appropriato del delegato.

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. Utilizzando indexPath di UITableView (per :) e una chiusura per delega

Questa è un'alternativa all'esempio precedente in cui utilizziamo una chiusura anziché una dichiarazione delegata dal protocollo per gestire il tocco dei pulsanti.

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. Usando accessoriesType di UITableViewCell e di UITableViewDelegate tableView(_:accessoryButtonTappedForRowWith:)

Se il tuo pulsante è un controllo accessorio standard di UITableViewCell , qualsiasi tocco su di esso attiverà una chiamata alla tabella di UITableViewDelegate (_: accessoriesButtonTappedForRowWith: ) , che consente di ottenere il percorso dell'indice correlato.

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

}

Funziona anche per me, grazie @Cocoanut

Ho trovato il metodo di utilizzo della superview della superview per ottenere un riferimento all'indicePath della cella funzionava perfettamente. Grazie a iphonedevbook.com (macnsmith) per il testo del link suggerimento

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

}

puoi usare il modello di tag:

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

Mi sto perdendo qualcosa? Non puoi semplicemente usare il mittente per identificare il pulsante. Il mittente ti fornirà informazioni come questa:

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

Quindi, se vuoi cambiare le proprietà del pulsante, pronuncia l'immagine di sfondo che hai appena detto al mittente:

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

Se hai bisogno del tag, il metodo di ACBurk va bene.

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

In realtà abbastanza semplice:

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

Funziona bene per me: P

se si desidera regolare la configurazione dell'azione target, è possibile includere il parametro dell'evento nel metodo, quindi utilizzare i tocchi di quell'evento per risolvere le coordinate del tocco. Le coordinate devono ancora essere risolte nei limiti della vista touch, ma ciò potrebbe sembrare più facile per alcune persone.

crea un array nsmutable e metti tutti i pulsanti in quell'array usando [array addObject: yourButton];

nel metodo premere il pulsante

-

 (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

}
}

Una leggera variazione sulla risposta di Cocoanuts (che mi ha aiutato a risolverlo) quando il pulsante era in fondo a una tabella (che ti impedisce di trovare la "cella cliccata":

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

Uso sempre i tag.

Devi sottoclassare UITableviewCell e gestire la pressione del pulsante da lì.

È semplice; crea una cella personalizzata e prendi uno sbocco del pulsante

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

    cell.yourButton.tag = indexPath.Row;

- (void)buttonPressedAction:(id)sender

cambia ID nel metodo sopra in (UIButton *)

Puoi ottenere il valore di quale pulsante viene toccato facendo sender.tag.

Sottoclasse il pulsante per memorizzare il valore richiesto, magari creare un protocollo (ControlWithData o qualcosa del genere). Imposta il valore quando aggiungi il pulsante alla cella della vista tabella. Nel tuo evento di ritocco, controlla se il mittente obbedisce al protocollo ed estrae i dati. Di solito memorizzo un riferimento all'oggetto reale che viene visualizzato nella cella della vista tabella.

SWIFT 2 AGGIORNAMENTO

Ecco come scoprire quale pulsante è stato toccato + invia dati a un altro ViewController dal indexPath.row di quel pulsante, dato che presumo che sia il punto per la maggior parte!

@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")

    }

Per coloro che stanno usando una classe ViewController e hanno aggiunto un TableView, sto usando un ViewController invece di un TableViewController, quindi ho aggiunto manualmente tableView per accedervi.

Ecco il codice per passare i dati a un altro VC quando si tocca quel pulsante e si passa la cella 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)

}

Nota che sto usando una cella personalizzata, questo codice funziona perfettamente per me

 @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 soluzione di Chris Schwerdt ma poi in Swift ha funzionato per me:

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

Questo problema ha due parti:

1) Ottenere il percorso dell'indice di UITableViewCell che contiene UIButton

premuto

Ci sono alcuni suggerimenti come:

  • Aggiornamento del UIButton tag nel metodo cellForRowAtIndexPath: utilizzando il valore row del percorso dell'indice. Questa non è una buona soluzione in quanto richiede l'aggiornamento continuo di tag e non funziona con le viste delle tabelle con più di una sezione.

  • Aggiungendo una proprietà NSIndexPath alla cella personalizzata e aggiornandola invece del tag di UIButton in cellForRowAtIndexPath: metodo. Questo risolve il problema di più sezioni ma non è ancora buono in quanto richiede l'aggiornamento sempre.

  • Mantenere un debole riferimento a UITableView nella cella personalizzata durante la creazione e utilizzando il metodo indexPathForCell: per ottenere il percorso dell'indice. Sembra un po 'meglio, non è necessario aggiornare nulla nel metodo cellForRowAtIndexPath: , ma richiede comunque l'impostazione di un riferimento debole quando viene creata la cella personalizzata.

  • Utilizzo della proprietà superView della cella per ottenere un riferimento al UITableView principale. Non è necessario aggiungere alcuna proprietà alla cella personalizzata e non è necessario impostare / aggiornare nulla al momento della creazione / successiva. Ma il superView della cella dipende dai dettagli di implementazione di iOS. Quindi non può essere utilizzato direttamente.

Ma questo può essere ottenuto usando un semplice ciclo, poiché siamo sicuri che la cella in questione debba trovarsi in un UITableView:

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

Quindi, questi suggerimenti possono essere combinati in un metodo di cella personalizzato semplice e sicuro per ottenere il percorso dell'indice:

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

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

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

D'ora in poi, questo metodo può essere utilizzato per rilevare quale UIButton viene premuto.

2) Informare le altre parti sull'evento di pressione dei pulsanti

Dopo aver saputo internamente quale UIButton viene premuto in quale cella personalizzata con percorso dell'indice esatto, queste informazioni devono essere inviate ad altre parti (molto probabilmente il controller di visualizzazione che gestisce UITableView ). Pertanto, questo evento di clic sui pulsanti può essere gestito in un livello di astrazione e logica simile a didSelectRowAtIndexPath: del metodo del delegato UITableView.

Due approcci possono essere usati per questo:

a) Delega: la cella personalizzata può avere una proprietà delegato e può definire un protocollo. Quando si preme il pulsante, esegue i suoi metodi delegati sulla proprietà delegate . Ma questa proprietà delegate deve essere impostata per ogni cella personalizzata quando vengono create. In alternativa, la cella personalizzata può scegliere di eseguire i suoi metodi delegati anche sul delegato della sua tabella padre.

b) Centro di notifica: le celle personalizzate possono definire un nome di notifica personalizzato e pubblicare questa notifica con il percorso dell'indice e le informazioni di visualizzazione della tabella padre fornite nell'oggetto userInfo . Non è necessario impostare nulla per ogni cella, è sufficiente aggiungere un osservatore per la notifica della cella personalizzata.

Uso una soluzione che include la sottoclasse UIButton e ho pensato di condividerla qui, codici in Swift:

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

Quindi ricorda di aggiornare indexPath in 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
}

Quindi, quando si risponde all'evento del pulsante, è possibile utilizzarlo come

func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top