Mendeteksi UIButton mana yang ditekan dalam UITableView

212

Saya punya UITableViewdengan 5 UITableViewCells. Setiap sel berisi UIButtonyang diatur sebagai berikut:

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

Pertanyaan saya adalah ini: dalam buttonPressedAction:metode ini, bagaimana saya tahu tombol mana yang telah ditekan. Saya sudah mempertimbangkan menggunakan tag tetapi saya tidak yakin ini adalah rute terbaik. Saya ingin dapat memberi tag entah bagaimana indexPathke kontrol.

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

Apa cara standar untuk melakukan ini?

Edit:

Saya agak menyelesaikannya dengan melakukan hal berikut. Saya masih ingin memiliki pendapat apakah ini cara standar untuk melakukannya atau apakah ada cara yang lebih baik?

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

Yang penting untuk dicatat adalah bahwa saya tidak dapat menetapkan tag dalam pembuatan sel karena sel mungkin akan mati. Rasanya sangat kotor. Pasti ada cara yang lebih baik.

kendali
sumber
Saya tidak melihat masalah dengan menggunakan solusi tag Anda. Sel-sel digunakan kembali, jadi masuk akal untuk mengatur tag ke indeks baris seperti yang Anda lakukan di sini. Saya menemukan ini solusi yang jauh lebih elegan daripada mengubah lokasi sentuh menjadi indeks baris, seperti yang disarankan di bawah ini.
Erik van der Neut

Jawaban:

400

Dalam sampel Aksesori Apple metode berikut digunakan:

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

Kemudian pada touch handler sentuh koordinat diambil dan jalur indeks dihitung dari koordinat itu:

- (void)checkButtonTapped:(id)sender
{
    CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    if (indexPath != nil)
    {
     ...
    }
}
Vladimir
sumber
Ya inilah yang saya selesaikan (lihat edit saya). Saya setuju dengan Anda bahwa itu tidak optimal.
instal
2
Tetapi Anda menambahkan UIButton ke UITableViewCell sendiri sehingga Anda harus konsisten dengan apa yang Anda lakukan saat membuat sel. Meskipun pendekatan ini tidak benar-benar terlihat elegan saya harus akui
Vladimir
1
Untuk solusi pertama, Anda perlu mengambil [[tombol superview] superview] karena panggilan superview pertama akan memberi Anda contentView, dan akhirnya yang kedua akan memberi Anda UITableViewCell. Solusi kedua tidak berfungsi dengan baik jika Anda menambah / menghapus sel karena itu akan membatalkan indeks baris. Oleh karena itu, saya pergi dengan solusi pertama seperti yang dijelaskan dan itu bekerja dengan sempurna.
raidfive
3
Ini dengan andal akan memilih sel yang memiliki tombol: UIView * view = tombol; while (! [lihat isKindOfClass: [kelas UITableViewCell]]) {view = [view superview]}
Jacob Lyles
1
Ada jebakan saat menggunakan: [tombol addTarget: self action: @selector (checkButtonTapped :) forControlEvents: UIControlEventTouchUpInside]; karena addTarget: action: forControlEvents: akan menambahkan beberapa target dan tindakan duplikat ketika Anda menggulir tabel, itu tidak akan menghapus target dan tindakan sebelumnya, sehingga metode checkButtonTapped: akan dipanggil berkali-kali ketika Anda mengklik tombol. Anda sebaiknya menghapus target dan aksi sebelum menambahkannya
bandw
48

Saya menemukan metode menggunakan superview superview untuk mendapatkan referensi ke indexPath sel bekerja dengan sempurna. Terima kasih kepada iphonedevbook.com (macnsmith) untuk teks tautan tip

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

}
Kelapa
sumber
Cocoanut, fragmen kode Anda menunjuk saya ke arah yang benar untuk variasi saya sendiri tentang masalah ini. Terima kasih! Jika ada orang lain yang membutuhkannya, kasing khusus saya adalah tombol ada di sel khusus yang ditampilkan sebagai bagian dari footer. Saya akan menambahkan kode di bawah ini
perangkat lunak berkembang
Jika Anda (pembaca Stackoverflow) mencoba ini dan itu tidak berhasil untuk Anda, periksa apakah dalam implementasi Anda, UIButton Anda sebenarnya adalah cucu dari UITableViewCell Anda. Dalam implementasi saya, UIButton saya adalah anak langsung dari UITableViewCell saya, jadi saya harus mengeluarkan salah satu "superview" dalam kode Cocoanut, dan kemudian berfungsi.
Jon Schneider
29
Ini sangat, sangat salah dan rusak di versi OS yang lebih baru. Jangan berjalan di pohon superview yang bukan milik Anda.
Kenrik
2
Ini berfungsi untuk saya di bawah iOS 6, tetapi rusak di iOS 7. Tampaknya @KenrikMarch memiliki poin yang valid!
Jon Schneider
3
di iOS 7 itu 1 langkah lebih lanjut superview. misal [[[pengirim superview] superview] superView];
CW0007007
43

Begini cara saya melakukannya. Sederhana dan ringkas:

- (IBAction)buttonTappedAction:(id)sender
{
    CGPoint buttonPosition = [sender convertPoint:CGPointZero
                                           toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    ...
}
Chris Schwerdt
sumber
2
Lebih sederhana lagi: gunakan CGPointZeroalih-alih CGPointMake(0, 0);-)
Jakob W
Mudah digunakan. Selanjutnya, mudah menerjemahkannya ke Swift 3. Anda adalah yang terbaik :)
Francisco Romero
Diterjemahkan ini ke Swift di bawah. Solusi termudah yang bisa saya temukan. Terimakasih Chris!
Rutger Huijsmans
6

Menemukan solusi yang bagus untuk masalah ini di tempat lain, tanpa bermain-main dengan tag pada tombol:

- (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...
}
Alpinista
sumber
5
Dalam contoh ini tidak jelas dari mana Anda mendapatkan objek 'acara'.
Nick Ludlam
Ini adalah solusi yang saya gunakan. Menggunakan tag tidak dapat diprediksi saat menambahkan / menghapus baris karena indeksnya berubah. Juga,
raidfive
@NickLudlam: mungkin nama metode tidak buttonPressedAction:tapi buttonPressedAction:forEvent:.
KPM
5

Bagaimana dengan mengirim informasi seperti NSIndexPathpada UIButtoninjeksi runtime using.

1) Anda perlu runtime saat mengimpor

2) tambahkan konstanta statis

3) tambahkan NSIndexPathke tombol Anda saat runtime menggunakan:

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

4) pada tombol tekan dapatkan metadata menggunakan:

(id) metaData: (id) target

Nikmati

    #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
    }
magno cardona
sumber
1
JIKA tabel disusun ulang atau baris dihapus maka ini tidak akan berfungsi.
Neil
5

Untuk melakukan (@Vladimir) jawaban Swift:

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

Meskipun memeriksa indexPath != nilmemberi saya jari ... "NSIndexPath bukan subtipe NSString"

dennis
sumber
5

Dengan Swift 4.2 dan iOS 12, Anda dapat memilih satu dari 5 contoh lengkap berikut untuk menyelesaikan masalah Anda.


# 1. Menggunakan UIView's convert(_:to:)dan 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. Menggunakan UIView's convert(_:to:)dan UITableView' s indexPathForRow(at:)(alternatif)

Ini merupakan alternatif dari contoh sebelumnya di mana kita meneruskan nilke targetparameter dalam addTarget(_:action:for:). Dengan cara ini, jika responden pertama tidak melaksanakan tindakan, itu akan dikirim ke responden berikutnya dalam rantai responden sampai sampai implementasi yang tepat ditemukan.

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. Menggunakan UITableView's indexPath(for:)dan pola delegasi

Dalam contoh ini, kami menetapkan pengontrol tampilan sebagai delegasi sel. Ketika tombol sel diketuk, itu memicu panggilan ke metode delegasi yang sesuai.

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 Menggunakan UITableView's indexPath(for:)dan penutupan untuk delegasi

Ini adalah alternatif dari contoh sebelumnya di mana kami menggunakan penutupan alih-alih deklarasi protokol-delegasi untuk menangani ketuk tombol.

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 Menggunakan UITableViewCell's accessoryTypedan UITableViewDelegate' stableView(_:accessoryButtonTappedForRowWith:)

Jika tombol Anda adalah UITableViewCell's kontrol aksesori standar, setiap tap pada itu akan memicu panggilan untuk UITableViewDelegate' s tableView(_:accessoryButtonTappedForRowWith:), memungkinkan Anda untuk mendapatkan indeks jalan terkait.

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

}
Imanou Petit
sumber
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")
    }
Ankit Bansal
sumber
3

Saya akan menggunakan properti tag seperti yang Anda katakan, mengatur tag seperti:

[button setTag:indexPath.row];

lalu masukkan tag ke dalam tombolTekanAction seperti:

((UIButton *)sender).tag

Atau

UIButton *button = (UIButton *)sender; 
button.tag;
ACBurk
sumber
5
Pendekatan ini benar-benar rusak untuk tabel dengan bagian.
ohhorob
tidak, Anda bisa menggunakan beberapa fungsi sederhana untuk meletakkan bagian di tag juga.
ACBurk
2
tagadalah bilangan bulat. tampaknya agak kikuk untuk encoding / decoding jalur indeks ke tag tampilan.
ohhorob
Itu benar, tetapi ini adalah solusi, meskipun bukan yang akan saya gunakan jika saya memiliki bagian. Yang saya coba katakan adalah itu bisa dilakukan menggunakan metode ini, itu tidak rusak. Versi yang lebih baik, lebih kompleks akan menentukan jalur indeks dari posisi tombol di dalam UITableView. Namun, karena rein mengatakan ia hanya memiliki lima sel (tanpa bagian), itu mungkin membuat metode itu lebih rumit dan komentar awal Anda dan seluruh utas komentar ini tidak ada gunanya.
ACBurk
3

Meskipun saya menyukai cara tag ... jika Anda tidak ingin menggunakan tag untuk alasan apa pun, Anda dapat membuat anggota NSArraytombol premade:

NSArray* buttons ;

kemudian buat tombol-tombol itu sebelum merender tableView dan dorong mereka ke dalam array.

Kemudian di dalam tableView:cellForRowAtIndexPath:fungsi yang dapat Anda lakukan:

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

Kemudian dalam buttonPressedAction:fungsinya, Anda bisa melakukannya

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

UNTUK MENANGANI BAGIAN - Saya menyimpan NSIndexPath di UITableViewCell khusus

DI CLKIndexPricesHEADERTableViewCell.xib

DALAM IB Tambahkan UIButton ke XIB - DONT tambahkan tindakan!

Tambahkan outlet @ properti (retain, nonatomik) IBOutlet UIButton * tombolIndexSectionClose;

JANGAN CTRL + MENGURANGI tindakan dalam IB (dilakukan dalam kode di bawah)

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

Dalam viewForHeaderInSection (juga harus bekerja untuk cellForRow .... dll jika tabel Anda hanya memiliki 1 bagian)

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

... gunakan bagian ini untuk mendapatkan data untuk sel Anda

... isi

   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 menekan tombol DELETE pada header Section dan ini panggilan

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

Dalam contoh ini saya menambahkan tombol Hapus sehingga harus menunjukkan UIAlertView untuk mengonfirmasi

Saya menyimpan bagian dan memasukkan ke kamus menyimpan informasi tentang bagian dalam ivar di 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);
    }
}
brian.clear
sumber
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; //:)
}
mmmanish
sumber
Ini sedikit bermasalah karena indexPath sel dapat berubah, jika Anda memanggil deleteRowsAtIndexPaths.
John Gibb
deleteRowsAtIndexPaths akan menyebabkan cellForRowAtIndexPath dipanggil kembali. Kemudian tombol akan memiliki indexPaths baru yang benar.
mmmanish
1

Ini juga berfungsi untuk saya, Terima kasih @Cocoanut

Saya menemukan metode menggunakan superview superview untuk mendapatkan referensi ke indexPath sel bekerja dengan sempurna. Terima kasih kepada iphonedevbook.com (macnsmith) untuk teks tautan tip

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

}
pengguna366584
sumber
0

Anda dapat menggunakan pola 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)
}
Nir Levy
sumber
Bagaimana saya menandai kontrol jika saya memiliki banyak kontrol pada satu sel?
instal
Saya tidak yakin ini akan berhasil - jika sel dibuat untuk baris # 1 maka akan mendapatkan tag 1. Jika mendapat dequeued untuk baris # 3 maka masih akan memiliki tag 1, bukan 3.
instal
kira Anda benar tentang komentar kedua. salahku. Saya pikir solusi terbaik Anda adalah dengan subclass UIButton, tambahkan satu atau dua properti Anda sendiri, dan kemudian atur / dapatkan dalam kasus yang sesuai (tetap dengan tag: 1 yang Anda miliki dalam kode Anda)
Nir Levy
0

Apakah saya melewatkan sesuatu? Tidak bisakah Anda menggunakan pengirim untuk mengidentifikasi tombol. Pengirim akan memberi Anda info seperti ini:

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

Kemudian jika Anda ingin mengubah properti tombol, ucapkan gambar latar belakang yang baru saja Anda beri tahu pengirim:

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

Jika Anda membutuhkan tag maka metode ACBurk baik-baik saja.

Michael Morrison
sumber
1
Mereka mencari "objek" yang berhubungan dengan tombol
ohhorob
0
// how do I know which button sent this message?
// processing button press for this row requires an indexPath.

Sebenarnya cukup mudah:

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

Bekerja dengan baik untuk saya: P

Jika Anda ingin menyesuaikan pengaturan aksi target, Anda dapat memasukkan parameter event dalam metode, dan kemudian menggunakan sentuhan event tersebut untuk menyelesaikan koordinat sentuhan. Koordinat masih perlu diselesaikan dalam batas tampilan sentuh, tetapi itu mungkin tampak lebih mudah bagi sebagian orang.

ohhorob
sumber
0

buat nsmutable array dan letakkan semua tombol di array itu ke [array addObject: yourButton];

dalam metode tekan tombol

-

 (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

}
}
rajesh
sumber
0

Sedikit variasi pada jawaban Cocoanuts (yang membantu saya menyelesaikan masalah ini) ketika tombol berada di bagian bawah tabel (yang mencegah Anda menemukan 'sel yang diklik':

-(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];
}
perangkat lunak berkembang
sumber
0

Saya selalu menggunakan tag.

Anda perlu subklas UITableviewCelldan menangani tombol tekan dari sana.

Chris
sumber
Saya tidak begitu mengerti caranya. Properti tag diatur selama pembuatan sel - sel ini dapat digunakan kembali untuk setiap baris dengan pengidentifikasi yang sama. Tag ini khusus untuk kontrol dalam sel generik yang dapat digunakan kembali. Bagaimana saya bisa menggunakan tag ini untuk membedakan tombol dalam sel yang dibuat secara umum? Bisakah Anda memposting beberapa kode?
instal
0

Itu mudah; membuat sel khusus dan mengambil outlet tombol

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

    cell.yourButton.tag = indexPath.Row;

- (void)buttonPressedAction:(id)sender

ubah id dalam metode di atas menjadi (UIButton *)

Anda bisa mendapatkan nilai tombol mana yang sedang diketuk dengan melakukan sender.tag.

piyush Bageria
sumber
0

Subkelas tombol untuk menyimpan nilai yang diperlukan, mungkin membuat protokol (ControlWithData atau sesuatu). Tetapkan nilai saat Anda menambahkan tombol ke sel tampilan tabel. Dalam acara touch up Anda, lihat apakah pengirim mematuhi protokol dan mengekstrak data. Saya biasanya menyimpan referensi ke objek aktual yang diberikan pada sel tampilan tabel.

Jerome Chan Yeow Heong
sumber
0

UPDATE SWIFT 2

Inilah cara untuk mengetahui tombol mana yang disadap + mengirim data ke ViewController lain dari tombol itu indexPath.rowkarena saya berasumsi itulah intinya!

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

    }

Bagi mereka yang menggunakan kelas ViewController dan menambahkan tableView, saya menggunakan ViewController daripada TableViewController jadi saya secara manual menambahkan tableView untuk mengaksesnya.

Berikut adalah kode untuk meneruskan data ke VC lain ketika mengetuk tombol itu dan melewati sel 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)

}
Lukesivi
sumber
0

Catatan di sini saya menggunakan sel khusus kode ini berfungsi dengan baik untuk saya

 @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);
    }
Gaurav
sumber
0

Solusi Chris Schwerdt tetapi kemudian di Swift bekerja untuk saya:

@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)
}
Rutger Huijsmans
sumber
0

Masalah ini memiliki dua bagian:

1) Mendapatkan jalur indeks UITableViewCellyang berisi penekananUIButton

Ada beberapa saran seperti:

  • Memperbarui UIButton's tagdi cellForRowAtIndexPath:metode menggunakan indeks jalan ini rownilai. Ini bukan solusi yang baik karena membutuhkan pembaruan tagterus-menerus dan tidak bekerja dengan tampilan tabel dengan lebih dari satu bagian.

  • Menambahkan NSIndexPathproperti ke sel adat dan memperbarui itu bukannya UIButton's tagdi cellForRowAtIndexPath:metode. Ini menyelesaikan beberapa bagian masalah tetapi masih tidak bagus karena selalu membutuhkan pembaruan.

  • Menyimpan referensi yang lemah ke induk UITableViewdi sel khusus sambil membuat dan menggunakan indexPathForCell:metode untuk mendapatkan jalur indeks. Tampaknya sedikit lebih baik, tidak perlu memperbarui apa pun dalam cellForRowAtIndexPath:metode, tetapi masih memerlukan pengaturan referensi yang lemah ketika sel kustom dibuat.

  • Menggunakan superViewproperti sel untuk mendapatkan referensi ke induk UITableView. Tidak perlu menambahkan properti apa pun ke sel khusus, dan tidak perlu mengatur / memperbarui apa pun pada kreasi / nanti. Tetapi sel superViewtergantung pada detail implementasi iOS. Jadi tidak bisa digunakan secara langsung.

Tetapi ini dapat dicapai dengan menggunakan loop sederhana, karena kami yakin sel yang dimaksud harus dalam UITableView:

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

Jadi, saran ini dapat digabungkan menjadi metode sel kustom yang sederhana dan aman untuk mendapatkan jalur indeks:

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

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

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

Mulai sekarang, metode ini dapat digunakan untuk mendeteksi yang UIButtonditekan.

2) Memberitahu pihak lain tentang acara tekan tombol

Setelah secara internal mengetahui yang UIButtonditekan di mana sel khusus dengan jalur indeks yang tepat, informasi ini perlu dikirim ke pihak lain (kemungkinan besar pengendali tampilan menangani UITableView). Jadi, acara klik tombol ini dapat ditangani dalam tingkat abstraksi dan logika yang serupa dengan didSelectRowAtIndexPath:metode delegasi UITableView.

Dua pendekatan dapat digunakan untuk ini:

a) Delegasi: sel khusus dapat memiliki delegateproperti dan dapat mendefinisikan protokol. Ketika tombol ditekan itu hanya menjalankan metode pendelegasian pada delegateproperti itu. Tetapi delegateproperti ini perlu disetel untuk setiap sel khusus ketika dibuat. Sebagai alternatif, sel kustom dapat memilih untuk melakukan metode delegasinya pada tampilan tabel induknya delegatejuga.

b) Pusat Pemberitahuan: sel khusus dapat menentukan nama pemberitahuan kustom dan memposting pemberitahuan ini dengan jalur indeks dan informasi tampilan tabel induk yang disediakan dalam userInfoobjek. Tidak perlu mengatur apa pun untuk setiap sel, cukup menambahkan pengamat untuk notifikasi sel kustom sudah cukup.

erkanyildiz
sumber
0

Saya menggunakan solusi yang subclass UIButtondan saya pikir saya harus membagikannya di sini, kode di Swift:

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

Maka ingatlah untuk memperbarui indexPath di 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
}

Jadi ketika merespons acara tombol Anda dapat menggunakannya seperti

func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
Ben Ong
sumber