Wykrywanie, który przycisk UIB został naciśnięty w UITableView


212

Mam UITableViewz 5 UITableViewCells. Każda komórka zawiera UIButtonktóry jest ustawiony w następujący sposób:

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

Moje pytanie brzmi: w buttonPressedAction:metodzie skąd mam wiedzieć, który przycisk został naciśnięty. Rozważałem użycie tagów, ale nie jestem pewien, czy to najlepsza trasa. Chciałbym móc jakoś oznaczyć ten indexPathelement sterujący.

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

Jaki jest standardowy sposób to zrobić?

Edytować:

W pewnym sensie rozwiązałem to, wykonując następujące czynności. Wciąż chciałbym mieć opinię, czy jest to standardowy sposób na zrobienie tego, czy jest lepszy sposób?

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

Ważne jest, aby zauważyć, że nie mogę ustawić znacznika podczas tworzenia komórki, ponieważ zamiast tego komórkę można usunąć z kolejki. Czuje się bardzo brudny. Musi być lepszy sposób.


Nie widzę problemu z użyciem rozwiązania tagów. Komórki są ponownie używane, więc warto ustawić znacznik na indeks wierszy w sposób, w jaki to robisz. Uważam to za o wiele bardziej eleganckie rozwiązanie niż przekształcanie lokalizacji dotykowej na indeks wierszy, jak sugerowano poniżej.
Erik van der Neut

Odpowiedzi:


400

W przykładzie akcesoriów Apple używana jest następująca metoda:

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

Następnie w manipulatorze dotykowym pobrano współrzędną dotykową i od tej współrzędnej obliczana jest ścieżka indeksu:

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

Tak, na tym właśnie się zdecydowałem (zobacz moją edycję). Zgadzam się z tobą, że to nie jest optymalne.
wodza

2
Ale dodajesz UIButton do UITableViewCell, więc musisz być spójny z tym, co robisz podczas tworzenia komórki. Chociaż to podejście nie wygląda naprawdę elegancko, muszę przyznać
Vladimir

1
W przypadku pierwszego rozwiązania konieczne będzie pobranie [[podglądu przycisku] podglądu], ponieważ pierwsze wywołanie podglądu da ci contentView, a na koniec drugie da UITableViewCell. Drugie rozwiązanie nie działa dobrze, jeśli dodajesz / usuwasz komórki, ponieważ spowoduje to unieważnienie indeksu wiersza. Dlatego wybrałem pierwsze rozwiązanie zgodnie z opisem i zadziałało idealnie.
raidfive

3
To niezawodnie wybierze komórkę, która jest właścicielem przycisku: UIView * view = button; while (! [view isKindOfClass: [UITableViewCell class]]) {view = [view superview]}
Jacob Lyles

1
Przy użyciu: [przycisk addTarget: self action: @selector (checkButtonTapped :) forControlEvents: UIControlEventTouchUpInside] istnieje pułapka; ponieważ addTarget: action: forControlEvents: doda wiele zduplikowanych celów i akcji podczas przewijania tabeli, nie usunie poprzednich celów i akcji, więc metoda checkButtonTapped: będzie wywoływana wiele razy po kliknięciu przycisku. Lepiej usuń cel i akcję przed ich dodaniem
bandw

48

Znalazłem metodę używania superwizji superwizji w celu uzyskania odniesienia do indeksu komórki Ścieżka działała idealnie. Dzięki iphonedevbook.com (macnsmith) za tekst linku do końcówki

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

}

Cocoanut, twój fragment kodu wskazał mi właściwy kierunek dla moich własnych wariantów tego problemu. Dzięki! Na wypadek, gdyby ktoś tego potrzebował, moim specjalnym przypadkiem było to, że przycisk znajdował się w niestandardowej komórce wyświetlanej jako część stopki. Dodam kod poniżej
oprogramowanie ewoluowało

Jeśli Ty (czytnik Stackoverflow) wypróbujesz to i nie zadziała to dla ciebie, sprawdź, czy w twojej implementacji Twój UIButton jest faktycznie wnukiem twojej UITableViewCell. W mojej implementacji mój UIButton był bezpośrednim dzieckiem mojego UITableViewCell, więc musiałem wyjąć jeden z „superview” w kodzie Cocoanut, a potem zadziałało.
Jon Schneider

29
Jest to bardzo, bardzo złe i jest zepsute w nowszych wersjach systemu operacyjnego. Nie chodź po drzewach, które nie masz.
Kenrik, marzec

2
To działało dla mnie pod iOS 6, ale jest zepsute w iOS 7. Wygląda na to, że @KenrikMarch ma rację!
Jon Schneider,

3
w iOS 7 jest to jeszcze jeden krok w górę w superwizji. np. [[[nadzór nadawcy] superview] superView];
CW0007007

43

Oto jak to robię. Prosty i zwięzły:

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

2
Jeszcze prostsze: użyj CGPointZerozamiast CGPointMake(0, 0);-)
Jakob W

Łatwy w obsłudze. Co więcej, łatwo przetłumaczyć go na Swift 3. Jesteś najlepszy :)
Francisco Romero,

Przetłumaczyłem to na Swift poniżej. Najłatwiejsze rozwiązanie, jakie mogłem znaleźć. Dzięki Chris!
Rutger Huijsmans

6

Znalazłem dobre rozwiązanie tego problemu gdzie indziej, bez bałaganu dzięki tagom na przycisku:

- (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
W tym przykładzie nie jest jasne, skąd pochodzi obiekt „zdarzenie”.
Nick Ludlam

To jest rozwiązanie, z którym poszedłem. Używanie tagów jest nieprzewidywalne podczas dodawania / usuwania wierszy, ponieważ zmieniają się ich indeksy. Ponadto,
raidfive

@NickLudlam: prawdopodobnie nazwa metody nie jest, buttonPressedAction:ale buttonPressedAction:forEvent:.
KPM

5

Co powiesz na przesłanie informacji, tak jak NSIndexPathprzy UIButtonużyciu zastrzyku środowiska wykonawczego.

1) Potrzebujesz środowiska uruchomieniowego podczas importu

2) dodaj stałą statyczną

3) dodaj NSIndexPathdo przycisku w czasie wykonywania, używając:

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

4) po naciśnięciu przycisku pobierz metadane, używając:

(id) metaData: (id) cel

Cieszyć się

    #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
JEŚLI tabela zostanie zmieniona lub wiersz zostanie usunięty, to nie zadziała.
Neil

5

Odpowiedź na czyn (@Vladimir) jest szybka:

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

Chociaż sprawdzanie pod kątem indexPath != nildaje mi palec ... „NSIndexPath nie jest podtypem NSString”


5

W Swift 4.2 i iOS 12 możesz wybrać jeden z 5 następujących kompletnych przykładów , aby rozwiązać problem.


# 1. Używanie UIViewconvert(_:to:)i UITableViewindexPathForRow(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. Używanie UIViewconvert(_:to:)i UITableViewindexPathForRow(at:)(alternatywa)

Jest to alternatywa dla poprzedniego przykładu, w którym przechodzimy nildo targetparametru w addTarget(_:action:for:). W ten sposób, jeśli pierwszy responder nie zaimplementuje akcji, zostanie wysłany do następnego respondera w łańcuchu respondentów, dopóki nie zostanie znaleziona poprawna implementacja.

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. Korzystanie UITableView„s indexPath(for:)i przenieść wzór

W tym przykładzie ustawiliśmy kontroler widoku jako delegata komórki. Po naciśnięciu przycisku komórki uruchamia połączenie z odpowiednią metodą uczestnika.

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. Za pomocąUITableView „s indexPath(for:)oraz zamknięcie dla delegacji

Jest to alternatywa dla poprzedniego przykładu, w którym do obsługi naciśnięcia przycisku używamy zamknięcia zamiast deklaracji delegowania protokołu.

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. Korzystanie UITableViewCellzaccessoryType i UITableViewDelegatetableView(_:accessoryButtonTappedForRowWith:)

Jeśli przycisk jest UITableViewCell„s standardowe wyposażenie sterowania, każdy z kranu na nim spowoduje wywołanie UITableViewDelegate” s tableView(_:accessoryButtonTappedForRowWith:), co pozwala uzyskać powiązany ścieżkę indeksu.

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

Chciałbym użyć właściwości tagu, jak powiedziałeś, ustawiając tag tak:

[button setTag:indexPath.row];

następnie umieszczenie tagu w przycisku ButtonPressedAction w następujący sposób:

((UIButton *)sender).tag

Lub

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

5
To podejście jest całkowicie zepsute w przypadku tabel z sekcjami.
ohhorob

nie, możesz po prostu użyć prostej funkcji, aby umieścić sekcję w znaczniku.
ACBurk

2
tagjest liczbą całkowitą. trochę niezdarne jest kodowanie / dekodowanie ścieżek indeksu do znaczników widoku.
ohhorob

Zgadza się, ale jest to rozwiązanie, choć nie takie, którego użyłbym, gdybym miał sekcje. Próbowałem tylko powiedzieć, że można to zrobić za pomocą tej metody, że nie zostało zepsute. Lepsza, bardziej złożona wersja określałaby ścieżkę indeksu na podstawie pozycji przycisku w UITableView. Jednak ponieważ rein powiedział, że ma tylko pięć komórek (bez sekcji), prawdopodobnie komplikuje to tę metodę, a twój początkowy komentarz i cały wątek komentarza są bezcelowe.
ACBurk

3

Chociaż podoba mi się sposób tagowania ... jeśli nie chcesz używać tagów z jakiegokolwiek powodu, możesz utworzyć członka NSArraygotowych przycisków:

NSArray* buttons ;

następnie utwórz te przyciski przed renderowaniem tableView i wepchnij je do tablicy.

Następnie wewnątrz tableView:cellForRowAtIndexPath:funkcji możesz wykonać:

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

Następnie w buttonPressedAction:funkcji możesz to zrobić

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

2

OBSŁUGA SEKCJI - Przechowałem NSIndexPath w niestandardowym UITableViewCell

W CLKIndexPricesHEADERTableViewCell.xib

IN IB Dodaj UIButton do XIB - NIE dodawaj akcji!

Dodaj wylot @ właściwość (zachowaj, nieatomowy) IBOutlet UIButton * przyciskIndexSectionClose;

NIE CTRL + PRZECIĄGNIJ akcji w IB (wykonanej w kodzie poniżej)

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

In viewForHeaderInSection (powinien również działać dla cellForRow .... itd., Jeśli tabela ma tylko 1 sekcję)

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

... skorzystaj z sekcji, aby uzyskać dane dla swojej komórki

...wypełnij

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

UŻYTKOWNIK naciska przycisk DELETE na nagłówku sekcji i to wywołuje

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

W tym przykładzie dodałem przycisk Usuń, więc powinienem pokazać UIAlertView, aby to potwierdzić

Przechowuję sekcję i wprowadzam do słownika, przechowując informacje o sekcji w ivar w 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; //:)
}

Jest to nieco wadliwe, ponieważ indeksPath komórki może się zmienić, jeśli wywołasz deleteRowsAtIndexPaths.
John Gibb,

deleteRowsAtIndexPaths spowoduje ponowne wywołanie cellForRowAtIndexPath. Następnie przyciski będą miały nowe poprawne ścieżki indeksu.
mmmanishs

1

Działa to również dla mnie, dzięki @Cocoanut

Znalazłem metodę używania superwizji superwizji w celu uzyskania odniesienia do indeksu komórki Ścieżka działała idealnie. Dzięki iphonedevbook.com (macnsmith) za tekst linku do końcówki

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

}

0

możesz użyć wzorca tagu:

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

Jak oznaczyć kontrole, gdybym miał wiele kontroli w jednej komórce?
wodza

Nie jestem pewien, że to będzie działać - jeśli komórka zostanie stworzony dla rzędu # 1 to będzie uzyskać tag 1. Jeżeli rozkolejkowywana do wiersza nr 3 to będzie jeszcze tag 1, nie 3.
Rein

chyba masz rację co do drugiego komentarza. mój błąd. Myślę, że najlepszym rozwiązaniem jest podklasy UIButton, dodać inną nieruchomość lub dwa własne, a następnie ustawić / uzyskać je w odpowiednich przypadkach (kij z tagiem: 1 miałeś w kodzie)
Nir Levy

0

Czy coś brakuje? Nie możesz po prostu użyć nadawcy do zidentyfikowania przycisku. Nadawca poda następujące informacje:

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

Następnie, jeśli chcesz zmienić właściwości przycisku, powiedz obraz tła, który właśnie przekazujesz nadawcy:

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

Jeśli potrzebujesz tagu, metoda ACBurk jest w porządku.


1
Szukają swojego „obiektu”, do którego odnosi się przycisk
ohhorob

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

Właściwie całkiem proste:

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

Działa dla mnie dobrze: P

jeśli chcesz dostosować ustawienia akcji docelowej, możesz uwzględnić parametr zdarzenia w metodzie, a następnie użyć akcentów tego zdarzenia, aby ustalić współrzędne dotyku. Współrzędne nadal wymagają rozwiązania w granicach widoku dotykowego, ale dla niektórych osób może to wydawać się łatwiejsze.


0

utwórz tablicę nsmutable i umieść w niej wszystkie przyciski usint [array addObject: yourButton];

w metodzie naciśnięcia przycisku

-

 (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

Niewielka zmiana odpowiedzi Cocoanuts (która pomogła mi rozwiązać ten problem), gdy przycisk znajdował się w stopce tabeli (co uniemożliwia znalezienie „klikniętej komórki”):

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

Zawsze używam tagów.

Musisz podklasować UITableviewCelli obsługiwać naciśnięcie przycisku stamtąd.


Nie bardzo rozumiem jak. Właściwość znacznika jest ustawiana podczas tworzenia komórki - ta komórka jest wielokrotnego użytku dla każdego wiersza z tym samym identyfikatorem. Ten znacznik jest specyficzny dla kontroli w ogólnej komórce wielokrotnego użytku. Jak mogę użyć tego znacznika do rozróżnienia przycisków w komórkach, które zostały utworzone w ogólny sposób? Czy możesz napisać kod?
powstrzymać

0

To proste; utwórz niestandardową komórkę i wyjmij guzik

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

    cell.yourButton.tag = indexPath.Row;

- (void)buttonPressedAction:(id)sender

zmień identyfikator w powyższej metodzie na (UIButton *)

Możesz uzyskać wartość, którą przycisk jest naciskany, wykonując sender.tag.


0

Podklasuj przycisk, aby zapisać wymaganą wartość, może utworzyć protokół (ControlWithData lub coś takiego). Ustaw wartość po dodaniu przycisku do komórki widoku tabeli. W zdarzeniu retuszu sprawdź, czy nadawca przestrzega protokołu i wyodrębnij dane. Zwykle przechowuję odwołanie do rzeczywistego obiektu renderowanego w komórce widoku tabeli.


0

AKTUALIZACJA SWIFT 2

Oto, jak dowiedzieć się, który przycisk został naciśnięty + wysłać dane do innego ViewController z tego przycisku, indexPath.rowponieważ zakładam, że o to chodzi w większości!

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

    }

Dla tych, którzy używają klasy ViewController i dodali tableView, używam ViewController zamiast TableViewController, więc ręcznie dodałem tableView, aby uzyskać do niego dostęp.

Oto kod do przekazywania danych do innego VC po dotknięciu tego przycisku i przekazaniu komórek 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

Uwaga: używam niestandardowej komórki, ten kod działa dla mnie idealnie

 @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

Rozwiązanie Chrisa Schwerdta, ale wtedy w Swift działało dla mnie:

@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

Ten problem składa się z dwóch części:

1) Uzyskanie UITableViewCellwciśniętej ścieżki indeksuUIButton

Istnieje kilka sugestii, takich jak:

  • Aktualizacja UIButtondydaktycznego tagw cellForRowAtIndexPath:sposób stosując ścieżkę indeksu może rowwartość. To nie jest dobre rozwiązanie, ponieważ wymaga tagciągłej aktualizacji i nie działa z widokami tabel z więcej niż jedną sekcją.

  • Dodawanie NSIndexPathwłaściwość niestandardową komórki i jej uaktualnianie zamiast UIButton„s tagw cellForRowAtIndexPath:metodzie. Rozwiązuje to problem z wieloma sekcjami, ale nadal nie jest dobry, ponieważ wymaga zawsze aktualizacji.

  • Utrzymywanie słabego refrenu nadrzędnego UITableVieww niestandardowej komórce podczas jej tworzenia i używanie indexPathForCell:metody w celu uzyskania ścieżki indeksu. Wydaje się nieco lepiej, nie trzeba niczego aktualizować w cellForRowAtIndexPath:metodzie, ale nadal wymaga ustawienia słabego odwołania podczas tworzenia komórki niestandardowej.

  • Korzystanie z superViewwłaściwości komórki w celu uzyskania odwołania do elementu nadrzędnego UITableView. Nie ma potrzeby dodawania żadnych właściwości do niestandardowej komórki i nie trzeba niczego ustawiać / aktualizować podczas tworzenia / później. Ale komórka superViewzależy od szczegółów implementacji iOS. Dlatego nie można go używać bezpośrednio.

Ale można to osiągnąć za pomocą prostej pętli, ponieważ jesteśmy pewni, że komórka, o której mowa, musi znajdować się w UITableView:

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

Tak więc te sugestie można połączyć w prostą i bezpieczną niestandardową metodę komórki do uzyskania ścieżki indeksu:

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

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

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

Odtąd ta metoda może być używana do wykrywania, który UIButtonzostanie naciśnięty.

2) Informowanie innych stron o zdarzeniu naciśnięcia przycisku

Po wewnętrznym poznaniu, w którym UIButtonnaciśnięto komórkę niestandardową z dokładną ścieżką indeksu, informacje te należy przesłać innym podmiotom (najprawdopodobniej kontrolerowi obsługi UITableView). Tak więc zdarzenie kliknięcia przycisku może być obsługiwane na poziomie abstrakcyjnym i logicznym podobnym do didSelectRowAtIndexPath:metody delegowania UITableView.

W tym celu można zastosować dwa podejścia:

a) Delegowanie: komórka niestandardowa może mieć delegatewłaściwość i może definiować protokół. Po naciśnięciu przycisku po prostu wykonuje swoje metody delegowania na swojej delegatewłaściwości. Ale tę delegatewłaściwość należy ustawić dla każdej niestandardowej komórki podczas ich tworzenia. Alternatywnie komórka niestandardowa może również wykonywać swoje metody delegowania w widoku tabeli nadrzędnej delegate.

b) Centrum powiadomień: niestandardowe komórki mogą zdefiniować niestandardową nazwę powiadomienia i opublikować to powiadomienie, podając w indeksie ścieżkę indeksu i informacje o widoku tabeli nadrzędnej userInfo. Nie trzeba niczego ustawiać dla każdej komórki, wystarczy tylko dodać obserwatora do powiadomienia komórki niestandardowej.


0

Korzystam z rozwiązania, które podklasę, UIButtoni pomyślałem, że powinienem je tutaj udostępnić, kody w Swift:

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

Następnie pamiętaj, aby zaktualizować jego indexPath w 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
}

Tak więc, odpowiadając na zdarzenie przycisku, możesz go używać w podobny sposób

func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.