Dapatkan klik tombol di dalam UITableViewCell

140

Saya memiliki pengontrol tampilan dengan tampilan tabel dan pena terpisah untuk templat sel tabel. Templat sel memiliki beberapa tombol. Saya ingin mengakses klik tombol bersama dengan indeks sel yang diklik di dalam view controller di mana saya telah mendefinisikan tampilan Table.

Jadi saya memiliki ViewController.hdan di ViewController.mmana saya memiliki UITableViewdan TableTemplate.h, TableTemplate.mdan di TableTemplate.xibmana saya memiliki biji didefinisikan. Saya ingin acara klik tombol dengan indeks sel di ViewController.m.

Ada bantuan tentang bagaimana saya bisa melakukan itu?

ankit_rck
sumber

Jawaban:

258

1) Dalam cellForRowAtIndexPath:metode Anda , tetapkan tag tombol sebagai indeks:

cell.yourbutton.tag = indexPath.row;

2) Tambahkan target dan aksi untuk tombol Anda seperti di bawah ini:

[cell.yourbutton addTarget:self action:@selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];

3) Tindakan kode berdasarkan indeks seperti di bawah ini di ViewControler:

-(void)yourButtonClicked:(UIButton*)sender
{
     if (sender.tag == 0) 
     {
         // Your code here
     }
}

Pembaruan untuk banyak Bagian:

Anda dapat memeriksa tautan ini untuk mendeteksi klik tombol pada tampilan tabel untuk beberapa baris dan bagian.

Mani
sumber
1
Ini juga dapat dilakukan melalui Interface Builder (IB) di langkah kedua. Pastikan tag tombol Anda diatur. Anda benar-benar tidak ingin mencampurkan panggilan tindakan Anda. Baik melakukannya melalui IB atau melakukannya secara eksplisit dalam kode Anda.
Sententia
@Mani Tidak merusak MVC - aksinya ada di TableView bukan Cell.
davecom
@ dviecom Jika Anda menetapkan target tombol sebagai sel (melalui IB), Bagaimana pemicu dari tableView? Atau apakah cara mereka untuk menghubungkan target tombol ke tampilan tabel yang ditempatkan di xib sel?
Mani
24
Solusi ini mengalami masalah ketika Anda mulai memasukkan dan menghapus baris. Tag tidak diperbarui ketika baris digeser. Alih-alih menyimpan referensi ke baris. Mungkin lebih baik menyimpan referensi ke id objek unik.
Vincent Cheong
1
Kapan pun Anda menemukan diri Anda memberikan nilai pada atribut tag tampilan, Anda memiliki bau kode yang sangat buruk yang dapat menggigit Anda nanti. Cari cara yang lebih baik untuk mencapai tujuan Anda, bukan pos SO pertama yang Anda temukan.
TigerCoding
148

Delegasi adalah cara untuk pergi.

Seperti yang terlihat dengan jawaban lain menggunakan tampilan mungkin sudah ketinggalan zaman. Siapa tahu besok mungkin ada bungkus lain dan mungkin perlu digunakan cell superview]superview]superview]superview]. Dan jika Anda menggunakan tag, Anda akan mendapatkan nomor n jika kondisi lain untuk mengidentifikasi sel. Untuk menghindari semua itu mengatur delegasi. (Dengan melakukan itu, Anda akan membuat kelas sel yang dapat digunakan kembali. Anda bisa menggunakan kelas sel yang sama dengan kelas dasar dan yang harus Anda lakukan hanyalah menerapkan metode delegasi.)

Pertama kita membutuhkan antarmuka (protokol) yang akan digunakan oleh sel untuk berkomunikasi (mendelegasikan) klik tombol. ( Anda dapat membuat file .h terpisah untuk protokol dan termasuk di dalam pengontrol tampilan tabel dan kelas sel khusus ATAU tambahkan saja di kelas sel kustom yang akan tetap disertakan dalam pengontrol tampilan tabel )

@protocol CellDelegate <NSObject>
- (void)didClickOnCellAtIndex:(NSInteger)cellIndex withData:(id)data;
@end

Sertakan protokol ini di pengontrol tampilan sel dan tabel kustom. Dan pastikan pengontrol tampilan tabel mengonfirmasi protokol ini.

Di sel khusus buat dua properti:

@property (weak, nonatomic) id<CellDelegate>delegate;
@property (assign, nonatomic) NSInteger cellIndex;

Dalam UIButtondelegasi IBAction, klik: ( Hal yang sama dapat dilakukan untuk tindakan apa pun di kelas sel kustom yang perlu didelegasikan kembali untuk melihat pengontrol )

- (IBAction)buttonClicked:(UIButton *)sender {
    if (self.delegate && [self.delegate respondsToSelector:@selector(didClickOnCellAtIndex:withData:)]) {
        [self.delegate didClickOnCellAtIndex:_cellIndex withData:@"any other cell data/property"];
    }
}

Di pengontrol tampilan tabel cellForRowAtIndexPathsetelah mengeluarkan sel, atur properti di atas.

cell.delegate = self;
cell.cellIndex = indexPath.row; // Set indexpath if its a grouped table.

Dan mengimplementasikan delegate di controller tampilan tabel:

- (void)didClickOnCellAtIndex:(NSInteger)cellIndex withData:(id)data
{
    // Do additional actions as required.
    NSLog(@"Cell at Index: %d clicked.\n Data received : %@", cellIndex, data);
}

Ini akan menjadi pendekatan yang ideal untuk mendapatkan tindakan tombol sel kustom di pengontrol tampilan tabel.

GoodSp33d
sumber
2
Mengapa Anda menjadikan delegasi sebagai properti kuat sel? Ini akan memberi Anda siklus penahan, kecuali Anda tahu pengontrol hanya memegang sel dengan lemah.
JulianSymes
bagaimana dengan beign _cellIndex diperbarui setelah sel dihapus?
skornos
2
Saya mendengar dari salah satu teman saya mengatakan menggunakan delegasi pada setiap sel menyebabkan konsumsi memori, jadi gunakan tag. Apakah ini benar?
Bista
2
periksa pertanyaan ini dude stackoverflow.com/questions/31649220/…
Nischal Hada
@the_UB Tidak ada banyak antara mengatur tag dan menyimpan satu referensi. Mungkin sebuah tag akan memakan lebih banyak memori.
Ian Warburton
66

Alih-alih bermain dengan tag, saya mengambil pendekatan yang berbeda. Membuat delegasi untuk subclass saya dari UITableViewCell (OptionButtonsCell) dan menambahkan indexPath var. Dari tombol saya di storyboard saya menghubungkan @IBAction ke OptionButtonsCell dan di sana saya mengirim metode delegasi dengan indexPath yang tepat kepada siapa pun yang tertarik. Dalam sel untuk jalur indeks saya mengatur indexPath saat ini dan berfungsi :)

Biarkan kode berbicara sendiri:

Swift 3 Xcode 8

OptionButtonsTableViewCell.swift

import UIKit
protocol OptionButtonsDelegate{
    func closeFriendsTapped(at index:IndexPath)
}
class OptionButtonsTableViewCell: UITableViewCell {
    var delegate:OptionButtonsDelegate!
    @IBOutlet weak var closeFriendsBtn: UIButton!
    var indexPath:IndexPath!
    @IBAction func closeFriendsAction(_ sender: UIButton) {
        self.delegate?.closeFriendsTapped(at: indexPath)
    }
}

MyTableViewController.swift

class MyTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, OptionButtonsDelegate {...

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "optionCell") as! OptionButtonsTableViewCell
    cell.delegate = self
    cell.indexPath = indexPath
    return cell   
}

func closeFriendsTapped(at index: IndexPath) {
     print("button tapped at index:\(index)")
}
Maciej Chrzastek
sumber
dapatkah Anda membantu saya, saya mendapatkan kesalahan di baris ini: class MyTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, OptionButtonsDelegate // error: Kesesuaian berlebihan dari 'MyTableViewController' ke protokol 'UITableViewDataSource'
Ulug'bek Ro'zimboyev
Sepertinya Anda mencoba menyesuaikan diri dengan UITableViewDataSource beberapa kali. Mungkin Anda sudah mendapatkan ekstensi di mana Anda sudah menyesuaikan diri dengan sumber data ?, tidak dapat membantu lebih banyak lagi tanpa kode
Maciej Chrzastek
1
dan bagaimana cara melewatkan data untuk melakukan segue dan pergi ke pengontrol tampilan lain?
Milad Faridnia
2
Solusi terbaik dan terbersih!
appsunited
31

Ini akan membantu: -

UITableViewCell* cell = (UITableViewCell*)[sender superview];
NSIndexPath* indexPath = [myTableView indexPathForCell:cell];

Di sini pengirim adalah contoh UIButton yang mengirim acara. myTableView adalah instance UITableView yang Anda hadapi.

Dapatkan referensi sel dengan benar dan semua pekerjaan selesai.

Anda mungkin perlu menghapus tombol dari contentView sel & menambahkannya langsung ke instance UITableViewCell karena subview.

Atau

Anda dapat merumuskan skema penamaan tag untuk berbagai UIButtons di cell.contentView. Dengan menggunakan tag ini, nanti Anda bisa mengetahui informasi baris & bagian sesuai kebutuhan.

Tarun
sumber
4
seharusnya [[pengirim superview] superview];
pierre23
2
Ini bagus untuk sel yang sangat sederhana. Namun, jika sel Anda memiliki pohon pandangan yang dalam, jawaban Mani adalah yang terbaik.
Sententia
3
Sekarang di iOS 7 seharusnya UITableViewCell * cell = (UITableViewCell *) [[[sender superview] superview] superview]; Terima kasih.
Rajan Maharjan
periksa pertanyaan ini dude stackoverflow.com/questions/31649220/…
Nischal Hada
22

Kode berikut mungkin Membantu Anda.

Saya telah mengambil UITableViewdengan kelas sel prototipe kustom bernama UITableViewCelldi dalamUIViewController .

Jadi saya punya ViewController.h, ViewController.mdan TableViewCell.h,TableViewCell.m

Ini kode untuk itu:

ViewController.h

@interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>

@property (strong, nonatomic) IBOutlet UITableView *tblView;

@end

ViewController.m

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return (YourNumberOfRows);
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *cellIdentifier = @"cell";

    __weak TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    if (indexPath.row==0) {
        [cell setDidTapButtonBlock:^(id sender)
         {
             // Your code here

         }];
    }    
    return cell;
}

Kelas sel khusus:

TableViewCell.h

@interface TableViewCell : UITableViewCell

@property (copy, nonatomic) void (^didTapButtonBlock)(id sender);

@property (strong, nonatomic) IBOutlet UILabel *lblTitle;
@property (strong, nonatomic) IBOutlet UIButton *btnAction;

- (void)setDidTapButtonBlock:(void (^)(id sender))didTapButtonBlock;

@end

dan

UITableViewCell.m

@implementation TableViewCell

- (void)awakeFromNib {
    // Initialization code
    [self.btnAction addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];

}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}
- (void)didTapButton:(id)sender {
    if (self.didTapButtonBlock)
    {
        self.didTapButtonBlock(sender);
    }
}

Catatan : Di sini saya telah mengambil semua UIControlsmenggunakan Storyboard.

Semoga itu bisa membantu Anda ... !!!

Piyush
sumber
Cara terbaik yang pernah
Daniel Raouf
15

Alasan saya suka teknik di bawah ini karena juga membantu saya untuk mengidentifikasi bagian dari tabel.

Tambahkan Tombol di sel cellForRowAtIndexPath:

 UIButton *selectTaskBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [selectTaskBtn setFrame:CGRectMake(15, 5, 30, 30.0)];
        [selectTaskBtn setTag:indexPath.section]; //Not required but may find useful if you need only section or row (indexpath.row) as suggested by MR.Tarun 
    [selectTaskBtn addTarget:self action:@selector(addTask:)   forControlEvents:UIControlEventTouchDown];
[cell addsubview: selectTaskBtn];

AddTask acara:

-(void)addTask:(UIButton*)btn
{
    CGPoint buttonPosition = [btn convertPoint:CGPointZero toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
    if (indexPath != nil)
    {
     int currentIndex = indexPath.row;
     int tableSection = indexPath.section;
    }
}

Berharap bantuan ini.

Yogesh Lolusare
sumber
periksa pertanyaan ini dude stackoverflow.com/questions/31649220/…
Nischal Hada
12

Gunakan penutupan Swift:

class TheCell: UITableViewCell {

    var tapCallback: (() -> Void)?

    @IBAction func didTap(_ sender: Any) {
        tapCallback?()
    }
}

extension TheController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: TheCell.identifier, for: indexPath) as! TheCell {
            cell.tapCallback = {
                //do stuff
            }
            return cell
    }
}
valexa
sumber
7

Kode Tarun tidak berfungsi di iOS7, karena struktur UITableViewCell berubah dan sekarang ia akan mendapatkan "UITableViewCellScrollView" sebagai gantinya.

Posting ini Mendapatkan UITableViewCell dengan superview di iOS 7 memiliki solusi yang bagus untuk membuat loop untuk menemukan tampilan induk yang benar, terlepas dari perubahan apa pun di masa mendatang dalam struktur. Itu bermuara membuat loop:

    UIView *superView = [sender superview];
    UIView *foundSuperView = nil;

    while (nil != superView && nil == foundSuperView) {
        if ([superView isKindOfClass:[UITableViewCell class]]) {
            foundSuperView = superView;
        } else {
            superView = superView.superview;
        }
    }

Tautan memiliki kode untuk solusi yang lebih dapat digunakan kembali, tetapi ini harus bekerja.

Stenio Ferreira
sumber
6

Cepat 2.2

Anda perlu menambahkan target untuk tombol itu.

myButton.addTarget(self, action: #selector(ClassName.FunctionName(_:), forControlEvents: .TouchUpInside)

FunctionName: terhubung // misalnya

Dan tentu saja Anda perlu mengatur tag tombol itu karena Anda menggunakannya.

myButton.tag = indexPath.row

Anda dapat mencapai ini dengan mensubclassing UITableViewCell. Gunakan itu di pembangun antarmuka, jatuhkan tombol pada sel itu, hubungkan melalui outlet dan di sana Anda pergi.

Untuk mendapatkan tag di fungsi yang terhubung:

func connected(sender: UIButton) {
    let buttonTag = sender.tag
    // Do any additional setup
}
Himanshu padia
sumber
6

Swift 3 dengan Penutupan

Solusi yang bagus adalah menggunakan penutupan dalam UITableViewCell kustom untuk memanggil kembali ke viewController untuk suatu tindakan.

Dalam sel:

final class YourCustomCell: UITableViewCell {

    var callbackClosure: (() -> Void)?

    // Configure the cell here
    func configure(object: Object, callbackClosure: (() -> Void)?) {
       self.callbackClosure = callbackClosure
    }


// MARK: - IBAction
extension YourCustomCell {
    @IBAction fileprivate func actionPressed(_ sender: Any) {
        guard let closure = callbackClosure else { return }
        closure()
    }
}

Dalam View Controller: Delegasi Tableview

extension YourViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard let cell: YourCustomCell = cell as? YourCustomCell else { return }
        cell.configure(object: object, callbackClosure: { [weak self] in
            self?.buttonAction()
        })
     }
 }

fileprivate extension YourViewController {

    func buttonAction() {
        // do your actions here 
    }
}
Sevy11
sumber
5

Saya merasa paling mudah untuk mensubclass tombol di dalam sel Anda (Swift 3):

class MyCellInfoButton: UIButton {
    var indexPath: IndexPath?
}

Di kelas sel Anda:

class MyCell: UICollectionViewCell {
    @IBOutlet weak var infoButton: MyCellInfoButton!
   ...
}

Di sumber data tampilan tabel atau tampilan koleksi, saat mengeluarkan sel, berikan tombol jalur indeksnya:

cell.infoButton.indexPath = indexPath

Jadi Anda bisa memasukkan kode ini ke dalam pengontrol tampilan tabel:

@IBAction func handleTapOnCellInfoButton(_ sender: MyCellInfoButton) {
        print(sender.indexPath!) // Do whatever you want with the index path!
}

Dan jangan lupa untuk mengatur kelas tombol di Interface Builder Anda dan menautkannya ke handleTapOnCellInfoButtonfungsi!


diedit:

Menggunakan injeksi ketergantungan. Untuk mengatur panggilan penutupan:

class MyCell: UICollectionViewCell {
    var someFunction: (() -> Void)?
    ...
    @IBAction func didTapInfoButton() {
        someFunction?()
    }
}

dan menyuntikkan penutupan dalam metode willDisplay dari delegasi tampilan koleksi:

 func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    (cell as? MyCell)?.someFunction = {
        print(indexPath) // Do something with the indexPath.
    }
}
yesleon
sumber
Pendekatan penutupan adalah cara paling cepat yang pernah saya lakukan untuk melakukan ini. Kerja bagus!
Clifton Labrum
5

Ini Berfungsi untukku.

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     UIButton *Btn_Play = (UIButton *)[cell viewWithTag:101];
     [Btn_Play addTarget:self action:@selector(ButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)ButtonClicked:(UIButton*)sender {
     CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.Tbl_Name];
     NSIndexPath *indexPath = [self.Tbl_Name indexPathForRowAtPoint:buttonPosition];
}
Yogesh Tarsariya
sumber
1
// Add action in cell for row at index path -tableView

cell.buttonName.addTarget(self, action: #selector(ViewController.btnAction(_:)), for: .touchUpInside)

// Button Action

  @objc func btnAction(_ sender: AnyObject) {



        var position: CGPoint = sender.convert(.zero, to: self.tableView)


        let indexPath = self.tableView.indexPathForRow(at: position)
        let cell: UITableViewCell = tableView.cellForRow(at: indexPath!)! as
        UITableViewCell




}
Hitesh Chauhan
sumber
1

untuk swift 4:

inside the cellForItemAt ,
   
cell.chekbx.addTarget(self, action: #selector(methodname), for: .touchUpInside)

then outside of cellForItemAt
@objc func methodname()
{
//your function code
}

Radhe Yadav
sumber
1

Jika Anda ingin meneruskan nilai parameter dari sel ke UIViewController menggunakan closure

//Your Cell Class
class TheCell: UITableViewCell {

    var callBackBlockWithParam: ((String) -> ()) = {_ in }

//Your Action on button
    @IBAction func didTap(_ sender: Any) {
        callBackBlockWithParam("Your Required Parameter like you can send button as sender or anything just change parameter type. Here I am passing string")
    }
}

//Your Controller
extension TheController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: TheCell.identifier, for: indexPath) as! TheCell {
            cell.callBackBlockWithParam = { (passedParamter) in 

             //you will get string value from cell class
                print(passedParamter)     
      }
            return cell
    }
}
prachit
sumber
0

@Mani jawaban baik, namun tag tampilan di dalam contentView sel sering digunakan untuk tujuan lain. Anda dapat menggunakan tag sel sebagai gantinya (atau tag contentView sel):

1) Dalam cellForRowAtIndexPath:metode Anda , tetapkan tag sel sebagai indeks:

cell.tag = indexPath.row; // or cell.contentView.tag...

2) Tambahkan target dan aksi untuk tombol Anda seperti di bawah ini:

[cell.yourbutton addTarget:self action:@selector(yourButtonClicked:) forControlEvents:UIControlEventTouchUpInside];

3) Buat metode yang mengembalikan deretan pengirim (terima kasih @Stenio Ferreira):

- (NSInteger)rowOfSender:(id)sender
{
    UIView *superView = sender.superview;
    while (superView) {
        if ([superView isKindOfClass:[UITableViewCell class]])
            break;
        else
            superView = superView.superview;
    }

    return superView.tag;
}

4) Tindakan kode berdasarkan indeks:

-(void)yourButtonClicked:(UIButton*)sender
{
     NSInteger index = [self rowOfSender:sender];
     // Your code here
}
Borzh
sumber
0

CustomTableCell.h adalah UITableViewCell:

@property (weak, nonatomic) IBOutlet UIButton *action1Button;
@property (weak, nonatomic) IBOutlet UIButton *action2Button;

MyVC.m setelah impor:

@interface MYTapGestureRecognizer : UITapGestureRecognizer
@property (nonatomic) NSInteger dataint;
@end

Di dalam "cellForRowAtIndexPath" di MyVC.m:

//CustomTableCell 
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

//Set title buttons
[cell.action1Button setTitle:[NSString stringWithString:NSLocalizedString(@"action1", nil)] forState:UIControlStateNormal];
[cell.action2Button setTitle:[NSString stringWithString:NSLocalizedString(@"action2", nil)] forState:UIControlStateNormal];

//Set visibility buttons
[cell.action1Button setHidden:FALSE];
[cell.action2Button setHidden:FALSE];

//Do 1 action
[cell.action1Button addTarget:self action:@selector(do1Action :) forControlEvents:UIControlEventTouchUpInside];

//Do 2 action
MYTapGestureRecognizer *action2Tap = [[MYTapGestureRecognizer alloc] initWithTarget:self action:@selector(do2Action :)];
cancelTap.numberOfTapsRequired = 1;
cancelTap.dataint = indexPath.row;
[cell.action2Button setUserInteractionEnabled:YES];
[cell.action2Button addGestureRecognizer:action2Tap];

MyVC.m:

-(void)do1Action :(id)sender{
//do some action that is not necessary fr data
}

-(void)do2Action :(UITapGestureRecognizer *)tapRecognizer{
MYTapGestureRecognizer *tap = (MYTapGestureRecognizer *)tapRecognizer;
numberTag = tap.dataint;
FriendRequest *fr = [_list objectAtIndex:numberTag];

//connect with a WS o do some action with fr data

//actualize list in tableView
 [self.myTableView reloadData];
}
Mer
sumber
-1
cell.show.tag=indexPath.row;
     [cell.show addTarget:self action:@selector(showdata:) forControlEvents:UIControlEventTouchUpInside];

-(IBAction)showdata:(id)sender
{
    UIButton *button = (UIButton *)sender;

    UIStoryboard *storyBoard;
    storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    SecondViewController *detailView = [storyBoard instantiateViewControllerWithIdentifier:@"SecondViewController"];

    detailView.string=[NSString stringWithFormat:@"%@",[_array objectAtIndex:button.tag]];

    [self presentViewController:detailView animated:YES completion:nil];

}
pengguna8132169
sumber