Rilevare quale UIButton è stato premuto in un UITableView


212

Ho un UITableViewcon 5 UITableViewCells. Ogni cella contiene un UIButtonche è 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 buttonPressedAction:metodo, 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 indexPathil 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?

Modificare:

L'ho risolto un po 'nel modo seguente. Vorrei 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.


Non vedo alcun problema con l'utilizzo della soluzione tag. Le celle vengono riutilizzate, quindi ha senso impostare il tag sull'indice di riga nel modo in cui lo stai facendo qui. Trovo che questa sia una soluzione molto più elegante rispetto alla conversione della posizione del tocco in un indice di riga, come suggerito di seguito.
Erik van der Neut,

Risposte:


400

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

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

Quindi nel gestore del tocco le coordinate del tocco recuperate e il percorso dell'indice vengono calcolati da quella coordinata:

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

Sì, questo è quello su cui mi sono deciso (vedi la mia modifica). Sono d'accordo con te che non è ottimale.
Rein

2
Ma aggiungi UIButton a UITableViewCell da solo, quindi devi essere coerente con quello che fai quando crei la cella. Anche se questo approccio non sembra davvero elegante, devo ammetterlo
Vladimir

1
Per la prima soluzione, dovrai prendere [[button superview] superview] poiché la prima chiamata di superview ti darà contentView, e infine la seconda ti darà UITableViewCell. La seconda soluzione non funziona bene se si aggiungono / rimuovono celle poiché invaliderà l'indice di riga. Pertanto, ho scelto la prima soluzione come indicato e ha funzionato perfettamente.
raidfive del

3
Questo sceglierà in modo affidabile la cella che possiede il pulsante: UIView * view = button; while (! [visualizza isKindOfClass: [classe UITableViewCell]]) {view = [visualizza superview]}
Jacob Lyles

1
C'è una trappola quando si utilizza: [button addTarget: self action: @selector (checkButtonTapped :) forControlEvents: UIControlEventTouchUpInside]; perché addTarget: action: forControlEvents: aggiungerà più target e azioni duplicati durante lo scorrimento della tabella, non rimuoverà i target e le azioni precedenti, quindi il metodo checkButtonTapped: verrà chiamato molte volte quando si fa clic sul pulsante. Faresti meglio a rimuovere il bersaglio e l'azione prima di aggiungerli
banda

48

Ho trovato il metodo di utilizzo della superview della superview per ottenere un riferimento all'indice della cellPath ha funzionato 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];
...

}

Cocoanut, il tuo frammento di codice mi ha indicato la giusta direzione per la mia variazione su questo problema. Grazie! Nel caso in cui qualcun altro ne abbia bisogno, il mio caso speciale era che il pulsante si trovava in una cella personalizzata che veniva visualizzata come piè di pagina. Aggiungerò il codice qui sotto
software si è evoluto

Se tu (lettore Stackoverflow) provi questo e non funziona per te, controlla se nella tua implementazione il tuo UIButton è in realtà il nipote di UITableViewCell. Nella mia implementazione, il mio UIButton era un figlio diretto del mio UITableViewCell, quindi avevo bisogno di eliminare una delle "superview" nel codice di Cocoanut, e poi ha funzionato.
Jon Schneider,

29
Questo è molto, molto sbagliato ed è rotto nelle nuove versioni del sistema operativo. Non camminare su alberi superview che non possiedi.
Kenrik marzo

2
Questo funzionava per me su iOS 6, ma è rotto su iOS 7. Sembra che @KenrikMarch abbia un punto valido!
Jon Schneider,

3
in iOS 7 è 1 ulteriore passaggio alla superview. ad esempio [[[mittente superview] superview] superView];
CW0007007,

43

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

2
Ancora più semplice: usare CGPointZeroinvece di CGPointMake(0, 0);-)
Jakob W

Facile da lavorare con esso. Inoltre, è facile tradurlo in Swift 3. Sei il migliore :)
Francisco Romero,

Tradotto in Swift in basso. La soluzione più semplice che ho trovato. Grazie Chris!
Rutger Huijsmans,

6

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

5
In questo esempio non è chiaro da dove si ottenga l'oggetto "evento".
Nick Ludlam,

Questa è la soluzione con cui sono andato. L'uso dei tag è imprevedibile quando si aggiungono / rimuovono le righe poiché i loro indici cambiano. Inoltre,
raidfive del

@NickLudlam: probabilmente il nome del metodo non è buttonPressedAction:ma buttonPressedAction:forEvent:.
KPM,

5

Che ne dite di inviare le informazioni, come NSIndexPathnel UIButtoniniezione runtime utilizzando.

1) È necessario il runtime sull'importazione

2) aggiungi costante statica

3) aggiungi NSIndexPathal tuo pulsante in fase di esecuzione utilizzando:

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

4) premendo il pulsante ottieni metadati usando:

(Id) Metadati: (id) bersaglio

Godere

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

1
Se la tabella viene riorganizzata o una riga eliminata, questo non funzionerà.
Neil,

5

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

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

Anche se il controllo per indexPath != nilmi dà il dito ... "NSIndexPath non è un sottotipo di NSString"


5

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


# 1. Usando UIView' convert(_:to:)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. Utilizzo di UIView's convert(_:to:)e UITableView' s indexPathForRow(at:)(alternativa)

Questa è un'alternativa all'esempio precedente in cui passiamo nilal targetparametro 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 viene trovata un'implementazione corretta.

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. Utilizzo UITableViewdel indexPath(for:)modello di deleghe e

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. Utilizzo UITableViewdi indexPath(for:)e una chiusura per la delega

Questa è un'alternativa all'esempio precedente in cui utilizziamo una chiusura anziché una dichiarazione del delegato di protocollo per gestire il tocco del pulsante.

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 UITableViewCell' accessoryTypee UITableViewDelegate'tableView(_:accessoryButtonTappedForRowWith:)

Se il pulsante è un UITableViewCell's di controllo in dotazione di serie, tutto il colpetto su di esso attiverà una chiamata a UITableViewDelegate' s tableView(_:accessoryButtonTappedForRowWith:), che consente di ottenere il relativo percorso dell'indice.

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

}

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

3

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 pulsante PressedAction in questo modo:

((UIButton *)sender).tag

O

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

5
Questo approccio è completamente rotto per le tabelle con sezioni.
ohhorob,

no, potresti semplicemente usare qualche semplice funzione per inserire anche la sezione nel tag.
ACBurk,

2
tagè un numero intero. sembra un po 'goffo essere la codifica / decodifica dei percorsi dell'indice nei tag di visualizzazione.
ohhorob,

È corretto, ma è una soluzione, sebbene non quella che userei se avessi delle sezioni. Tutto quello che stavo cercando di dire era che si poteva fare usando questo metodo, che non era rotto. Una versione migliore e più complessa determinerebbe il percorso dell'indice dalla posizione del pulsante all'interno di UITableView. Tuttavia, poiché rein ha detto che ha solo cinque celle (senza sezioni), probabilmente rende questo metodo troppo complicato e il tuo commento iniziale e l'intero thread di commenti inutili.
ACBurk,

3

Anche se mi piace il modo dei tag ... se non vuoi usare i tag per qualsiasi motivo, puoi creare un membro NSArraydi pulsanti predefiniti:

NSArray* buttons ;

quindi creare quei pulsanti prima di eseguire il rendering di tableView e inserirli nell'array.

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

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

Quindi nella buttonPressedAction:funzione, puoi farlo

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

2

PER GESTIONE DELLE SEZIONI - Ho archiviato NSIndexPath in un UITableViewCell personalizzato

IN CLKIndexPricesHEADERTableViewCell.xib

IN IB Aggiungi UIButton a XIB - NON aggiungere azione!

Aggiungi outlet @property (keep, nonatomic) 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 1 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 il tuo cellulare

... compilalo

   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 preme il pulsante DELETE 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 la memorizzazione delle 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);
    }
}

2
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; //:)
}

Questo è leggermente difettoso perché indexPath di una cella può cambiare, se si chiama deleteRowsAtIndexPaths.
John Gibb,

deleteRowsAtIndexPaths farà sì che cellForRowAtIndexPath venga chiamato di nuovo. Quindi i pulsanti avranno nuovi indexPath corretti.
mmmanishs

1

Funziona anche per me, grazie @Cocoanut

Ho trovato il metodo di utilizzo della superview della superview per ottenere un riferimento all'indice della cellPath ha funzionato 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];
...

}

0

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

Come taggerei i controlli se avessi più controlli su una singola cella?
Rein

Non sono sicuro che funzionerebbe - se la cella viene creata per la riga # 1, otterrà il tag 1. Se viene rimescolata per la riga # 3, avrà comunque un tag di 1, non 3.
rein

suppongo che tu abbia ragione riguardo al secondo commento. colpa mia. Penso che la tua soluzione migliore sia sottoclassare UIButton, aggiungere un'altra o due tue proprietà e quindi impostarle / ottenerle nei casi appropriati (attenersi al tag: 1 che avevi nel tuo codice)
Nir Levy,

0

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, dì 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.


1
Stanno cercando il loro "oggetto" a cui si riferisce il pulsante
ohhorob,

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

Abbastanza semplice in realtà:

- (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 l'impostazione 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.


0

creare un array nsmutable e inserire tutti i pulsanti in quell'array usint [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

}
}

0

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

0

Uso sempre i tag.

È necessario sottoclassare UITableviewCelle gestire la pressione del pulsante da lì.


Non capisco bene come. La proprietà tag viene impostata durante la creazione della cella: questa cella è riutilizzabile per ogni riga con lo stesso identificatore. Questo tag è specifico per il controllo in una cella riutilizzabile generica. Come posso usare questo tag per differenziare i pulsanti nelle celle che sono stati creati in modo generico? Potresti pubblicare del codice?
Rein

0

È 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.


0

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.


0

SWIFT 2 AGGIORNAMENTO

Ecco come scoprire quale pulsante è stato toccato + invia dati a un altro ViewController da quel pulsante, indexPath.rowdato 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 utilizzano una classe ViewController e hanno aggiunto un TableView, sto usando un ViewController invece di un TableViewController, quindi ho aggiunto manualmente il TableView per accedervi.

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

}

0

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

0

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

0

Questo problema ha due parti:

1) Ottenere il percorso dell'indice di UITableViewCellcui contiene premutoUIButton

Ci sono alcuni suggerimenti come:

  • L'aggiornamento UIButtonè tagnel cellForRowAtIndexPath:metodo usando il rowvalore del percorso dell'indice . Questa non è una buona soluzione in quanto richiede un aggiornamento tagcontinuo e non funziona con le viste di tabella con più di una sezione.

  • Aggiunta di una NSIndexPathproprietà alla cella personalizzata e aggiornamento al posto del metodo UIButton" tagin cellForRowAtIndexPath:". Questo risolve il problema di più sezioni ma non è ancora buono in quanto richiede l'aggiornamento sempre.

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

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

Ma questo può essere ottenuto utilizzando 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 UIButtonviene premuto.

2) Informare le altre parti sull'evento della pressione del pulsante

Dopo aver saputo internamente quale UIButtonviene 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 il UITableView). Quindi, questo evento di clic sul pulsante può essere gestito in un livello di astrazione e logica simile a quellodidSelectRowAtIndexPath: metodo del delegato UITableView.

A tale scopo possono essere utilizzati due approcci:

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

b) Centro notifiche: 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 userInfonell'oggetto. Non è necessario impostare nulla per ogni cella, è sufficiente aggiungere un osservatore per la notifica della cella personalizzata.


0

Uso una soluzione che include una sottoclasse UIButtone 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)
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.