Натисніть на кнопку всередині UITableViewCell


140

У мене є контролер подання з поданням таблиці та окремою ручкою для шаблону комірки таблиці. У шаблоні комірок є кілька кнопок. Я хочу отримати доступ до натискання кнопки разом з індексом клітинки, натиснутою всередині контролера перегляду, де я визначив подання таблиці.

Таким чином, у мене є, ViewController.hі ViewController.mде я маю UITableViewі TableTemplate.h, TableTemplate.mі TableTemplate.xibде я визначив ручку. Я хочу, щоб подія натискання кнопки з індексом комірки в ViewController.m.

Будь-яка допомога щодо того, як це зробити?

Відповіді:


258

1) У вашому cellForRowAtIndexPath:методі призначте тег кнопки як індекс:

cell.yourbutton.tag = indexPath.row;

2) Додайте ціль і дії для своєї кнопки, як показано нижче:

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

3) Дія коду на основі індексу, як показано нижче ViewControler:

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

Оновлення для декількох розділів:

Ви можете перевірити це посилання, щоб виявити натискання кнопки у вигляді таблиці для кількох рядків та розділів.


1
Це також можна зробити через Interface Builder (IB) на другому кроці. Просто переконайтеся, що встановлено тег кнопок. Ви дійсно не хочете змішувати ваші дії з викликом. Або зробіть це через IB або зробіть це явно у своєму коді.
Sententia

@Mani Це не порушує MVC - дія полягає в TableView, а не в комірці.
davecom

@davecom Якщо ви встановите ціль кнопки як клітинку (через ІБ), як це буде запущено з tableView? Або є який-небудь спосіб підключити цільову кнопку до перегляду столу, який розміщений у xib комірки?
Мані

24
Це рішення наштовхується на проблеми, коли ви починаєте вставляти та видаляти рядки. Тег не оновлюється при зміщенні рядків. Замість того, щоб зберігати посилання на рядок. Можливо, краще зберегти посилання на унікальний ідентифікатор об'єкта.
Вінсент Чонг

1
Щоразу, коли ви присвоїли значення атрибутам тегів поглядів, у вас дуже поганий запах коду, який може вас кусати згодом. Шукайте кращі способи досягнення своєї мети, а не першу публікацію, яку Ви знайдете.
TigerCoding

148

Делегати - це шлях.

Як видно з інших відповідей, використання поглядів може застаріти. Хто знає завтра, може бути ще одна обгортка і може знадобитися cell superview]superview]superview]superview]. І якщо ви використовуєте теги, ви отримаєте n число, якщо б інакше умови для ідентифікації комірки. Щоб уникнути всього цього, створіть делегатів. (Тим самим ви будете створювати повторно використовуваний клас комірок. Ви можете використовувати той самий клас комірок, що і базовий клас, і все, що вам потрібно зробити, це реалізувати делегатні методи.)

Спочатку нам потрібен інтерфейс (протокол), який буде використовуватися коміркою для зв'язку (делегування) натискань кнопки. ( Ви можете створити окремий .h файл для протоколу і включити його як в контролер перегляду таблиці, так і в користувацькі класи комірок АБО просто додати його до спеціального класу комірок, який так чи інакше включить до контролера подання таблиці )

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

Включіть цей протокол у спеціальний контролер осередків та таблиць. І переконайтесь, що контролер подання таблиці підтверджує цей протокол.

У користувацькій комірці створіть два властивості:

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

У UIButtonделегаті IBAction натисніть: ( Те ж саме можна зробити для будь-яких дій у користувацькому класі комірок, які потрібно делегувати назад, щоб переглянути контролер )

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

У контролері подання таблиці cellForRowAtIndexPathпісля видалення комірки встановіть вищевказані властивості.

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

І реалізуйте делегат в контролері подання таблиці:

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

Це був би ідеальний підхід для отримання власних кнопок дій у контролері подання таблиці.


2
Чому ви зробили делегата сильною властивістю клітини? Це дасть вам цикл збереження, якщо ви не знаєте, що контролер лише слабко утримує комірку.
JulianSymes

як щодо оновлення _cellIndex, оновленого після видалення комірки?
skornos

2
Я чув від одного свого друга, що використання делегата на кожній комірці спричиняє споживання пам'яті, тому використовуйте теги. Це правда?
Біста

2
перевірити це питання dude stackoverflow.com/questions/31649220/…
Nischal Hada

@the_UB Не може бути багато між встановленням тегу і збереженням однієї посилання. Можливо, тег зайняв би більше пам’яті.
Ian Warburton

66

Замість гри з тегами я застосував інший підхід. Зробив делегата для мого підкласу UITableViewCell (OptionButtonsCell) і додав varx index var. З моєї кнопки в раскадровці я підключив @IBAction до OptionButtonsCell і там посилаю метод делегата з правильним indexPath всім, хто цікавиться. У комірці для шляху до індексу я встановлюю поточний indexPath і він працює :)

Нехай код говорить сам за себе:

Швидкий 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)")
}

Ви можете мені допомогти, я отримую помилку в цьому рядку: class MyTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, OptionButtonsDelegate // Помилка: Надмірна відповідність "MyTableViewController" протоколу "UITableViewDataSource"
Ulug'bek Ro'zimboyev

схоже, ви намагаєтеся кілька разів відповідати UITableViewDataSource. Можливо , у вас є розширення , де ви вже відповідати джерелу даних?, Не можу більше без коду
Maciej Chrzastek

1
і як передати дані, щоб виконати segue та перейти до іншого контролера перегляду?
Мілад Фарідня

2
Найкраще і найчистіше рішення!
appsunited

31

Це має допомогти:

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

Тут відправник - екземпляр UIButton, який надсилає подію. myTableView - це екземпляр UITableView , з яким ви маєте справу.

Просто правильно знайдіть посилання на клітинку і вся робота виконана.

Можливо, вам доведеться видалити кнопки з contentView комірки та додати їх безпосередньо до екземпляра UITableViewCell, оскільки це субпідгляд.

Або

Ви можете сформулювати схему іменування тегів для різних UIButtons у cell.contentView. Використовуючи цей тег, пізніше ви можете знати інформацію про рядки та розділи за потребою.


4
це має бути [[нагляд відправника] нагляд];
pierre23

2
Це добре для дуже простих комірок. Однак якщо у вашій клітині є глибоке дерево поглядів, відповідь Мані найкраща.
Sententia

3
Тепер у iOS 7 має бути UITableViewCell * cell = (UITableViewCell *) [[[supernderview] superview] superview]; Дякую.
Раджан Махарджан

перевірити це питання dude stackoverflow.com/questions/31649220/…
Nischal Hada

22

Наступний код може допомогти вам.

Я взяв UITableViewіз користувацьким прототипом клас клітин, названий UITableViewCellвсередині UIViewController.

Так у мене є ViewController.h, ViewController.mі TableViewCell.h,TableViewCell.m

Ось код для цього:

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

Спеціальний клас комірок:

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

і

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

Примітка . Тут я взяв все, UIControlsвикористовуючи Storyboard.

Сподіваюся, що може вам допомогти ... !!!


Найкращий спосіб
Даніель Рауф

15

Причина, яка мені подобається нижче в техніці, тому що вона також допомагає мені визначити розділ таблиці.

Додати кнопку у комірці клітиниForRowAtIndexPath:

 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:

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

Сподіваємось, що це допоможе.


перевірити це питання dude stackoverflow.com/questions/31649220/…
Nischal Hada

12

Скористайтеся закриттями 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
    }
}

7

Код Таруна не працює на iOS7, оскільки структура UITableViewCell змінилася і тепер він отримає "UITableViewCellScrollView".

Ця публікація Отримання UITableViewCell з наглядом в iOS 7 має гарне рішення, створюючи цикл для пошуку правильного виду батьків, незалежно від будь-яких майбутніх змін у структурі. Це зводиться до створення циклу:

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

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

У посиланні є код для більш багаторазового використання, але це має працювати.


6

Швидкий 2.2

Вам потрібно додати ціль для цієї кнопки.

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

FunctionName: підключено // наприклад

І звичайно вам потрібно встановити тег цієї кнопки, оскільки ви її використовуєте.

myButton.tag = indexPath.row

Ви можете досягти цього, підкласифікуючи UITableViewCell. Використовуйте його в конструкторі інтерфейсів, опустіть кнопку на цю клітинку, підключіть її через розетку і там ви йдете.

Щоб отримати тег у підключеній функції:

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

6

Швидкий 3 із закриттям

Приємним рішенням є використання закриття у користувальницькому UITableViewCell для зворотного виклику до viewController для дії.

У комірці:

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

Контролер перегляду: Делегат таблиці

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

5

Мені найпростіше підкласировать кнопку всередині вашої клітини (Swift 3):

class MyCellInfoButton: UIButton {
    var indexPath: IndexPath?
}

У вашому класі клітин:

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

У джерелі даних подання перегляду таблиці або колекції під час видалення комірки дайте кнопці свій індексний шлях:

cell.infoButton.indexPath = indexPath

Таким чином, ви можете просто ввести цей код у свій контролер подання таблиці:

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

І не забудьте встановити клас кнопки у вашому Interface Builder та зв’язати його з handleTapOnCellInfoButtonфункцією!


відредаговано:

Використання ін'єкції залежності. Щоб налаштувати виклик закриття:

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

і ввести закриття в методі willDisplay делегата представлення колекції:

 func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    (cell as? MyCell)?.someFunction = {
        print(indexPath) // Do something with the indexPath.
    }
}

Підхід до закриття - це найшвидший спосіб, який я бачив. Хороша робота!
Кліфтон Лабрум

5

Його робота для мене.

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

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




}

1

для швидкого 4:

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

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


1

Якщо ви хочете передати значення параметра з комірки в UIViewController, використовуючи закриття, тоді

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

0

@Mani відповідь хороша, проте теги поглядів усередині contentView клітини часто використовуються для інших цілей. Ви можете використовувати тег комірки (замість тегу contentView комірки):

1) У вашому cellForRowAtIndexPath:методі призначте тег комірки як індекс:

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

2) Додайте ціль і дії для своєї кнопки, як показано нижче:

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

3) Створіть метод, який повертає рядок відправника (спасибі @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) Дії коду на основі індексу:

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

0

CustomTableCell.h - це UITableViewCell:

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

MyVC.m після імпорту:

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

Всередині "cellForRowAtIndexPath" у 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];
}

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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.