Erkennen, welcher UIButton in einer UITableView gedrückt wurde


212

Ich habe eine UITableViewmit 5 UITableViewCells. Jede Zelle enthält eine, UIButtondie wie folgt eingerichtet ist:

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

Meine Frage lautet: buttonPressedAction:Woher weiß ich bei der Methode, welche Taste gedrückt wurde? Ich habe überlegt, Tags zu verwenden, bin mir aber nicht sicher, ob dies der beste Weg ist. Ich möchte das irgendwie indexPathauf dem Steuerelement markieren können.

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

Was ist der Standard dafür?

Bearbeiten:

Ich habe es irgendwie gelöst, indem ich Folgendes getan habe. Ich würde immer noch gerne eine Meinung dazu haben, ob dies die Standardmethode ist oder ob es eine bessere gibt.

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

Es ist wichtig zu beachten, dass ich das Tag bei der Erstellung der Zelle nicht festlegen kann, da die Zelle stattdessen möglicherweise aus der Warteschlange entfernt wird. Es fühlt sich sehr schmutzig an. Es muss einen besseren Weg geben.


Ich sehe kein Problem mit der Verwendung Ihrer Tag-Lösung. Die Zellen werden wiederverwendet, daher ist es sinnvoll, das Tag so auf den Zeilenindex zu setzen, wie Sie es hier tun. Ich finde dies eine viel elegantere Lösung als die Konvertierung der Berührungsposition in einen Zeilenindex, wie unten vorgeschlagen.
Erik van der Neut

Antworten:


400

Im Apple Accessory- Beispiel wird die folgende Methode verwendet:

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

Anschließend wird im Touch-Handler die Touch-Koordinate abgerufen und der Indexpfad aus dieser Koordinate berechnet:

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

Ja, darauf habe ich mich festgelegt (siehe meine Bearbeitung). Ich stimme Ihnen zu, dass es nicht optimal ist.
Zügel

2
Sie fügen UITutViewCell jedoch selbst UIButton hinzu, sodass Sie nur mit dem übereinstimmen müssen, was Sie beim Erstellen einer Zelle tun. Obwohl dieser Ansatz nicht wirklich elegant aussieht, muss ich zugeben
Vladimir

1
Für die erste Lösung müssen Sie die [[Button Superview] Superview] abrufen, da der erste Superview-Aufruf die ContentView und der zweite die UITableViewCell liefert. Die zweite Lösung funktioniert nicht gut, wenn Sie Zellen hinzufügen / entfernen, da dadurch der Zeilenindex ungültig wird. Daher habe ich mich für die erste Lösung entschieden und sie hat perfekt funktioniert.
Raidfive

3
Dadurch wird die Zelle, der die Schaltfläche gehört, zuverlässig ausgewählt: UIView * view = button; while (! [Ansicht isKindOfClass: [UITableViewCell-Klasse]]) {view = [Ansichtsübersicht]}
Jacob Lyles

1
Bei Verwendung von: [button addTarget: self action: @selector (checkButtonTapped :) forControlEvents: UIControlEventTouchUpInside] gibt es eine Falle. Da addTarget: action: forControlEvents: beim Scrollen der Tabelle mehrere doppelte Ziele und Aktionen hinzufügt, werden die vorherigen Ziele und Aktionen nicht entfernt, sodass die Methode checkButtonTapped: beim Klicken auf die Schaltfläche mehrmals aufgerufen wird. Sie sollten das Ziel und die Aktion entfernen, bevor Sie sie hinzufügen
Bandw

48

Ich fand, dass die Methode, die Superview der Superview zu verwenden, um einen Verweis auf den indexPath der Zelle zu erhalten, perfekt funktionierte. Vielen Dank an iphonedevbook.com (macnsmith) für den Tipp- Link-Text

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

}

Cocoanut, Ihr Codefragment hat mich in die richtige Richtung für meine eigene Variation dieses Problems geführt. Vielen Dank! Für den Fall, dass jemand anderes es benötigt, war mein Sonderfall, dass sich die Schaltfläche in einer benutzerdefinierten Zelle befand, die als Teil der Fußzeile angezeigt wurde. Ich werde unten Code hinzufügen
Software entwickelt

Wenn Sie (Stackoverflow Reader) dies versuchen und es für Sie nicht funktioniert, überprüfen Sie, ob Ihr UIButton in Ihrer Implementierung tatsächlich das Enkelkind Ihrer UITableViewCell ist. In meiner Implementierung war mein UIButton ein direktes Kind meiner UITableViewCell, daher musste ich eine der "Übersichten" in Cocoanuts Code entfernen, und dann funktionierte es.
Jon Schneider

29
Dies ist so sehr, sehr falsch und ist in neueren Versionen des Betriebssystems kaputt. Gehen Sie nicht über Bäume, die Sie nicht besitzen.
Kenrik

2
Dies funktionierte für mich unter iOS 6, ist aber in iOS 7 defekt. Es scheint, dass @KenrikMarch einen gültigen Punkt hat!
Jon Schneider

3
In iOS 7 ist es ein weiterer Schritt in der Übersicht. zB [[[Absender Superview] Superview] SuperView];
CW0007007

43

Hier ist, wie ich es mache. Einfach und prägnant:

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

2
Noch einfacher: Verwendung CGPointZeroanstelle von CGPointMake(0, 0);-)
Jakob W

Einfach damit zu arbeiten. Weiter einfach zu Swift 3 zu übersetzen. Du bist der Beste :)
Francisco Romero

Übersetzte dies in Swift unten. Die einfachste Lösung, die ich finden konnte. Danke Chris!
Rutger Huijsmans

6

Ich habe an anderer Stelle eine gute Lösung für dieses Problem gefunden, ohne mit Tags auf der Schaltfläche herumzuspielen:

- (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 diesem Beispiel ist nicht klar, woher Sie das 'Ereignis'-Objekt beziehen.
Nick Ludlam

Dies ist die Lösung, mit der ich mich entschieden habe. Die Verwendung von Tags ist beim Hinzufügen / Entfernen von Zeilen nicht vorhersehbar, da sich deren Indizes ändern. Auch
Raidfive

@NickLudlam: wahrscheinlich der Methodenname ist nicht buttonPressedAction:aber buttonPressedAction:forEvent:.
KPM

5

Wie wäre es mit dem Senden der Informationen wie NSIndexPathin der UIButtonVerwendung der Laufzeitinjektion.

1) Sie benötigen Laufzeit für den Import

2) statische Konstante hinzufügen

3) Fügen Sie NSIndexPathIhrer Schaltfläche zur Laufzeit Folgendes hinzu:

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

4) Beim Drücken der Taste erhalten Sie Metadaten mit:

(id) metaData: (id) Ziel

Genießen

    #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
Wenn die Tabelle neu angeordnet oder eine Zeile gelöscht wird, funktioniert dies nicht.
Neil

5

Die Antwort von To (@Vladimir) lautet Swift:

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

Obwohl das Überprüfen nach indexPath != nilmir den Finger gibt ... "NSIndexPath ist kein Subtyp von NSString"


5

Mit Swift 4.2 und iOS 12 können Sie eines der 5 folgenden vollständigen Beispiele auswählen , um Ihr Problem zu lösen.


# 1. Verwenden von UIView's convert(_:to:)und UITableView' sindexPathForRow(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. Verwenden von UIView's convert(_:to:)und UITableView' s indexPathForRow(at:)(Alternative)

Dies ist eine Alternative zum vorherigen Beispiel, in dem wir nilan den targetParameter in übergeben addTarget(_:action:for:). Auf diese Weise wird der erste Antwortende, der die Aktion nicht implementiert, an den nächsten Antwortenden in der Antworterkette gesendet, bis eine ordnungsgemäße Implementierung gefunden wurde.

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. Verwenden von UITableView's indexPath(for:)und delegieren Muster

In diesem Beispiel legen wir den Ansichtscontroller als Delegaten der Zelle fest. Wenn Sie auf die Schaltfläche der Zelle tippen, wird die entsprechende Methode des Delegaten aufgerufen.

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. Verwenden von UITableView's indexPath(for:)und einem Abschluss für die Delegation

Dies ist eine Alternative zum vorherigen Beispiel, bei dem anstelle einer Protokolldelegatdeklaration ein Abschluss verwendet wird, um das Tippen auf die Schaltfläche zu handhaben.

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. Verwenden von UITableViewCell's accessoryTypeund UITableViewDelegate' stableView(_:accessoryButtonTappedForRowWith:)

Wenn Ihre Taste ist ein UITableViewCell‚s Standardzubehör Kontrolle, jeder Hahn an wird es einen Anruf auslösen UITableViewDelegates‘ tableView(_:accessoryButtonTappedForRowWith:), so dass Sie die damit verbundenen Indexpfad erhalten.

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

Ich würde die Tag-Eigenschaft wie gesagt verwenden und das Tag so einstellen:

[button setTag:indexPath.row];

Dann wird das Tag wie folgt in die buttonPressedAction eingefügt:

((UIButton *)sender).tag

Oder

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

5
Dieser Ansatz ist für Tabellen mit Abschnitten vollständig gebrochen.
Ohhorob

Nein, Sie können auch eine einfache Funktion verwenden, um den Abschnitt in das Tag einzufügen.
ACBurk

2
tagist eine ganze Zahl. Es scheint etwas ungeschickt zu sein, Indexpfade in Ansichts-Tags zu codieren / decodieren.
Ohhorob

Das ist richtig, aber es ist eine Lösung, obwohl ich sie nicht verwenden würde, wenn ich Abschnitte hätte. Ich wollte nur sagen, dass es mit dieser Methode möglich ist, dass es nicht kaputt ist. Eine bessere, komplexere Version würde den Indexpfad anhand der Position der Schaltfläche in UITableView bestimmen. Da Rein jedoch gesagt hat, dass er nur fünf Zellen (ohne Abschnitte) hat, macht es diese Methode wahrscheinlich überkompliziert und Ihren anfänglichen Kommentar und diesen ganzen Kommentarthread sinnlos.
ACBurk

3

Obwohl mir die Tag-Methode gefällt ... Wenn Sie aus irgendeinem Grund keine Tags verwenden möchten, können Sie ein Mitglied NSArraymit vorgefertigten Schaltflächen erstellen :

NSArray* buttons ;

Erstellen Sie dann diese Schaltflächen, bevor Sie die tableView rendern, und verschieben Sie sie in das Array.

Innerhalb der tableView:cellForRowAtIndexPath:Funktion können Sie dann Folgendes tun:

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

Dann können Sie in der buttonPressedAction:Funktion tun

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

2

ABSCHNITTE BEHANDELN - Ich habe den NSIndexPath in einer benutzerdefinierten UITableViewCell gespeichert

IN CLKIndexPricesHEADERTableViewCell.xib

IN IB UIButton zu XIB hinzufügen - Aktion NICHT hinzufügen!

Add outlet @property (beibehalten, nicht atomar) IBOutlet UIButton * buttonIndexSectionClose;

NICHT STRG + DRAG eine Aktion in IB (im Code unten ausgeführt)

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

In viewForHeaderInSection (sollte auch für cellForRow .... etc funktionieren, wenn Ihre Tabelle nur 1 Abschnitt enthält)

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

... verwenden Sie den Abschnitt, um Daten für Ihre Zelle abzurufen

...Füll es aus

   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 drückt die DELETE-Taste in einer Abschnittsüberschrift und dies ruft auf

- (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 diesem Beispiel habe ich eine Schaltfläche zum Löschen hinzugefügt, damit UIAlertView angezeigt wird, um dies zu bestätigen

Ich speichere den Abschnitt und den Schlüssel im Wörterbuch und speichere Informationen über den Abschnitt in einem Ivar im 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; //:)
}

Dies ist etwas fehlerhaft, da sich der Indexpfad einer Zelle ändern kann, wenn Sie deleteRowsAtIndexPaths aufrufen.
John Gibb

deleteRowsAtIndexPaths führt dazu, dass cellForRowAtIndexPath erneut aufgerufen wird. Dann haben die Schaltflächen neue korrekte Indexpfade.
mmmanishs

1

Es funktioniert auch bei mir, danke @Cocoanut

Ich fand, dass die Methode, die Superview der Superview zu verwenden, um einen Verweis auf den indexPath der Zelle zu erhalten, perfekt funktionierte. Vielen Dank an iphonedevbook.com (macnsmith) für den Tipp-Link-Text

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

}

0

Sie können das Tag-Muster verwenden:

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

Wie würde ich die Steuerelemente markieren, wenn ich mehrere Steuerelemente in einer einzelnen Zelle hätte?
Zügel

Ich bin mir nicht sicher, ob dies funktionieren würde - wenn die Zelle für Zeile 1 erstellt wird, erhält sie das Tag 1. Wenn sie für Zeile 3 aus der Warteschlange entfernt wird, hat sie immer noch das Tag 1, nicht 3.
Rein

Ich denke, Sie haben Recht mit dem zweiten Kommentar. mein Fehler. Ich denke, Ihre beste Lösung besteht darin, UIButton in eine Unterklasse zu unterteilen, eine oder zwei weitere Eigenschaften hinzuzufügen und diese dann in den entsprechenden Fällen festzulegen / abzurufen (bleiben Sie bei dem Tag: 1, das Sie in Ihrem Code hatten)
Nir Levy

0

Vermisse ich etwas Können Sie nicht einfach den Absender verwenden, um die Schaltfläche zu identifizieren? Der Absender gibt Ihnen folgende Informationen:

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

Wenn Sie dann die Eigenschaften der Schaltfläche ändern möchten, sagen Sie das Hintergrundbild, das Sie gerade dem Absender mitteilen:

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

Wenn Sie das Tag benötigen, ist die Methode von ACBurk in Ordnung.


1
Sie suchen nach ihrem "Objekt", auf das sich der Knopf bezieht
ohhorob

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

Eigentlich ziemlich einfach:

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

Funktioniert gut für mich: P.

Wenn Sie Ihr Zielaktions-Setup anpassen möchten, können Sie den Ereignisparameter in die Methode aufnehmen und dann die Berührungen dieses Ereignisses verwenden, um die Koordinaten der Berührung aufzulösen. Die Koordinaten müssen noch in den Grenzen der Berührungsansicht aufgelöst werden, aber das scheint für manche Menschen einfacher zu sein.


0

Erstellen Sie ein nsmutable-Array und setzen Sie alle Schaltflächen in dieses Array. usint [array addObject: yourButton];

in der Tastendruckmethode

- -

 (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

Eine geringfügige Abweichung von der Antwort von Cocoanuts (die mir bei der Lösung dieses Problems geholfen hat), als sich die Schaltfläche in der Fußzeile einer Tabelle befand (wodurch verhindert wird, dass Sie die angeklickte Zelle finden:

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

Ich benutze immer Tags.

Sie müssen die Unterklasse UITableviewCellund den Tastendruck von dort aus handhaben.


Ich verstehe nicht ganz wie. Die Tag-Eigenschaft wird während der Zellenerstellung eingerichtet. Diese Zelle kann für jede Zeile mit demselben Bezeichner wiederverwendet werden. Dieses Tag ist spezifisch für das Steuerelement in einer generischen wiederverwendbaren Zelle. Wie kann ich dieses Tag verwenden, um Schaltflächen in Zellen zu unterscheiden, die generisch erstellt wurden? Könnten Sie einen Code posten?
Zügel

0

Es ist einfach; Erstellen Sie eine benutzerdefinierte Zelle und nehmen Sie einen Knopf heraus

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

    cell.yourButton.tag = indexPath.Row;

- (void)buttonPressedAction:(id)sender

Ändern Sie die ID in der obigen Methode in (UIButton *)

Sie können den Wert ermitteln, auf den die Schaltfläche getippt wird, indem Sie sender.tag ausführen.


0

Unterordnen Sie die Schaltfläche, um den erforderlichen Wert zu speichern, und erstellen Sie möglicherweise ein Protokoll (ControlWithData oder ähnliches). Legen Sie den Wert fest, wenn Sie die Schaltfläche zur Tabellenansichtszelle hinzufügen. Überprüfen Sie in Ihrem Ausbesserungsereignis, ob der Absender das Protokoll befolgt, und extrahieren Sie die Daten. Normalerweise speichere ich einen Verweis auf das tatsächliche Objekt, das in der Tabellenansichtszelle gerendert wird.


0

SWIFT 2 UPDATE

Hier erfahren Sie, wie Sie herausfinden, auf welche Schaltfläche getippt wurde, und Daten von dieser Schaltfläche an einen anderen ViewController senden, indexPath.rowda ich davon ausgehe, dass dies der Punkt für die meisten ist!

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

    }

Für diejenigen, die eine ViewController-Klasse verwenden und eine tableView hinzugefügt haben, verwende ich einen ViewController anstelle eines TableViewControllers, sodass ich die tableView manuell hinzugefügt habe, um darauf zuzugreifen.

Hier ist der Code zum Übergeben von Daten an einen anderen VC, wenn Sie auf diese Schaltfläche tippen und die Zelle übergeben 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

Beachten Sie, dass ich hier eine benutzerdefinierte Zelle verwende. Dieser Code funktioniert perfekt für mich

 @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

Chris Schwerdts Lösung hat dann aber in Swift für mich funktioniert:

@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

Dieses Problem besteht aus zwei Teilen:

1) Abrufen des Indexpfads, der UITableViewCellgedrückt enthältUIButton

Es gibt einige Vorschläge wie:

  • Aktualisierung UIButton‚s tagin cellForRowAtIndexPath:Methode Indexpfad des mit rowWert. Dies ist keine gute Lösung, da eine tagkontinuierliche Aktualisierung erforderlich ist und nicht mit Tabellenansichten mit mehr als einem Abschnitt funktioniert.

  • Hinzufügen einer NSIndexPathEigenschaft zu einer benutzerdefinierten Zelle und Aktualisieren dieser Eigenschaft anstelle der Methode UIButton's tagin ' cellForRowAtIndexPath:. Dies löst das Problem mit mehreren Abschnitten, ist aber immer noch nicht gut, da es immer aktualisiert werden muss.

  • Beibehalten einer schwachen Referenz zum übergeordneten Element UITableViewin der benutzerdefinierten Zelle, während diese erstellt wird, und Verwenden der indexPathForCell:Methode zum Abrufen des Indexpfads . Scheint ein bisschen besser zu sein, es ist nicht erforderlich, irgendetwas in der cellForRowAtIndexPath:Methode zu aktualisieren , aber es ist immer noch erforderlich, eine schwache Referenz festzulegen, wenn die benutzerdefinierte Zelle erstellt wird.

  • Verwenden der Zelleigenschaft superView, um einen Verweis auf das übergeordnete Element abzurufen UITableView. Sie müssen der benutzerdefinierten Zelle keine Eigenschaften hinzufügen und müssen bei der Erstellung / später nichts festlegen / aktualisieren. Die Zellen superViewhängen jedoch von den Details der iOS-Implementierung ab. Es kann also nicht direkt verwendet werden.

Dies kann jedoch mit einer einfachen Schleife erreicht werden, da wir sicher sind, dass sich die betreffende Zelle in einer UITableView befinden muss:

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

Diese Vorschläge können also zu einer einfachen und sicheren benutzerdefinierten Zellenmethode kombiniert werden, um den Indexpfad abzurufen:

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

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

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

Von nun an kann mit dieser Methode erkannt werden, welche UIButtongedrückt wird.

2) Informieren anderer Parteien über das Drücken von Tasten

Nachdem intern bekannt ist, welche UIButtonin welcher benutzerdefinierten Zelle mit genauem Indexpfad gedrückt wird, müssen diese Informationen an andere Parteien gesendet werden (höchstwahrscheinlich an den View Controller, der die verwaltet UITableView). Daher kann dieses Schaltflächenklickereignis in einer ähnlichen Abstraktions- und didSelectRowAtIndexPath:Logikebene wie die Methode des UITableView-Delegaten behandelt werden.

Hierfür können zwei Ansätze verwendet werden:

a) Delegierung: Benutzerdefinierte Zellen können eine delegateEigenschaft haben und ein Protokoll definieren. Wenn die Taste gedrückt wird, werden nur die Delegierungsmethoden für die delegateEigenschaft ausgeführt. Diese delegateEigenschaft muss jedoch für jede benutzerdefinierte Zelle festgelegt werden, wenn sie erstellt wird. Alternativ kann eine benutzerdefinierte Zelle ihre Delegierungsmethoden auch für die übergeordnete Tabellenansicht ausführen delegate.

b) Benachrichtigungscenter: Benutzerdefinierte Zellen können einen benutzerdefinierten Benachrichtigungsnamen definieren und diese Benachrichtigung mit den im userInfoObjekt bereitgestellten Indexpfad- und übergeordneten Tabellenansichtsinformationen veröffentlichen . Es ist nicht erforderlich, für jede Zelle etwas festzulegen. Es reicht aus, nur einen Beobachter für die Benachrichtigung der benutzerdefinierten Zelle hinzuzufügen.


0

Ich verwende eine Lösung dieser Unterklasse UIButtonund dachte, ich sollte sie hier nur teilen, Codes in Swift:

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

Denken Sie dann daran, den Indexpfad in zu aktualisieren 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
}

Wenn Sie also auf das Ereignis der Schaltfläche reagieren, können Sie es wie folgt verwenden

func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.