Обнаружение, какая кнопка UIB была нажата в UITableView


212

У меня есть UITableViewс 5 UITableViewCells. Каждая ячейка содержит элемент, UIButtonкоторый настроен следующим образом:

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

Мой вопрос заключается в следующем: в buttonPressedAction:методе, как узнать, какая кнопка была нажата. Я рассмотрел использование тегов, но я не уверен, что это лучший маршрут. Я хотел бы иметь возможность как-то пометить indexPathна элемент управления.

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

Какой стандартный способ сделать это?

Редактировать:

Я вроде решил это следующим образом. Я все еще хотел бы иметь мнение, является ли это стандартным способом сделать это или есть лучший способ?

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

Важно отметить, что я не могу установить тег при создании ячейки, поскольку вместо этого ячейка может быть удалена. Это очень грязно. Должен быть лучший способ.


Я не вижу никаких проблем с использованием вашего решения тегов. Ячейки используются повторно, поэтому имеет смысл установить тег для индекса строки так, как вы это делаете здесь. Я считаю, что это гораздо более элегантное решение, чем преобразование местоположения касания в индекс строки, как предлагается ниже.
Эрик ван дер Нойт

Ответы:


400

В примере аксессуаров Apple используется следующий метод:

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

Затем в обработчике касания извлекается координата касания и из этой координаты вычисляется индексный путь:

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

Да, это то, что я остановился (см. Мое редактирование). Я согласен с вами, что это не оптимально.
узда

2
Но вы сами добавляете UIButton в UITableViewCell, поэтому вы просто должны соответствовать тому, что делаете при создании ячейки. Хотя этот подход не выглядит элегантно, я должен признать
Владимир

1
Для первого решения вам необходимо получить [[superview] суперпредставление кнопки], так как первый вызов superview даст вам contentView, и, наконец, второе даст вам UITableViewCell. Второе решение не работает, если вы добавляете / удаляете ячейки, поскольку оно делает недействительным индекс строки. Поэтому я пошел с первым решением, как изложено, и оно работало отлично.
raidfive

3
Это позволит надежно выделить ячейку, которой принадлежит кнопка: UIView * view = button; while (! [view isKindOfClass: [UITableViewCell class]]) {view = [просмотреть суперпредставление]}
Джейкоб Лайлс

1
При использовании возникает ловушка: [кнопка addTarget: self action: @selector (checkButtonTapped :) forControlEvents: UIControlEventTouchUpInside]; потому что addTarget: action: forControlEvents: добавит несколько дублированных целей и действий при прокрутке таблицы, он не удалит предыдущие цели и действия, поэтому метод checkButtonTapped: будет вызываться много раз при нажатии кнопки. Вам лучше удалить цель и действие, прежде чем добавлять их
bandw

48

Я обнаружил, что метод использования суперпредставления суперпредставления для получения ссылки на indexPath ячейки работает отлично. Спасибо iphonedevbook.com (macnsmith) за текст ссылки

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

}

Cocoanut, ваш фрагмент кода указал мне правильное направление для моей собственной вариации этой проблемы. Спасибо! На случай, если кому-то еще это понадобится, мой особый случай состоял в том, что кнопка была в пользовательской ячейке, которая отображалась как часть нижнего колонтитула. Я добавлю код ниже
программное обеспечение эволюционировало

Если вы (читатель Stackoverflow) попробуете это, и у вас это не сработает, проверьте, действительно ли ваша UIButton является внуком вашего UITableViewCell. В моей реализации мой UIButton был прямым потомком моего UITableViewCell, поэтому мне нужно было извлечь один из «суперпредставлений» в коде Кокоанута, и тогда это сработало.
Джон Шнайдер

29
Это очень, очень неправильно и нарушается в новых версиях ОС. Не ходите по деревьям, которые вам не принадлежат.
Кенрик март

2
Это работало для меня под iOS 6, но не работает в iOS 7. Похоже, что @KenrikMarch имеет правильную точку!
Джон Шнайдер

3
в iOS 7 это еще один шаг вверх по суперпредставлению. например, [[[superder отправителя] superview] superView];
CW0007007

43

Вот как я это делаю. Просто и лаконично:

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

2
Еще проще: используйте CGPointZeroвместо CGPointMake(0, 0);-)
Jakob W

С ним легко работать. Далее легко перевести его на Swift 3. Ты лучший :)
Франциско Ромеро

Переведено это в Swift внизу. Самое простое решение, которое я мог найти. Спасибо Крис!
Rutger Huijsmans

6

Нашел хорошее решение этой проблемы в другом месте, не возиться с тегами на кнопке:

- (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
В этом примере неясно, откуда вы получаете объект 'event'.
Ник Лудлам

Это решение, которое я выбрал. Использование тегов непредсказуемо при добавлении / удалении строк, так как их индексы изменяются. Кроме того,
raidfive

@NickLudlam: вероятно, имя метода нет, buttonPressedAction:но buttonPressedAction:forEvent:.
KPM

5

Как насчет отправки информации, как NSIndexPath вUIButton использовании инъекции выполнения.

1) Вам нужно время выполнения при импорте

2) добавить статическую константу

3) добавить NSIndexPath к вашей кнопке во время выполнения, используя:

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

4) при нажатии кнопки получить метаданные, используя:

(ID) Metadata: (ID) целевой

наслаждаться

    #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
Если таблица переставлена ​​или строка удалена, это не сработает.
Нил

5

To to (@Vladimir) отвечает: Swift:

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

Хотя проверка indexPath != nilдает мне палец ... "NSIndexPath не является подтипом NSString"


5

В Swift 4.2 и iOS 12 вы можете выбрать один из 5 следующих полных примеров , чтобы решить вашу проблему.


# 1. Использование UIView's convert(_:to:)и UITableView'indexPathForRow(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. Использование UIView's convert(_:to:)и UITableView' s indexPathForRow(at:)(альтернатива)

Это альтернатива предыдущего примера , когда мы переходим nilк targetпараметру addTarget(_:action:for:). Таким образом, если первый респондент не реализует действие, оно будет отправлено следующему респонденту в цепочке респондентов до тех пор, пока не будет найдена правильная реализация.

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. Использование UITableView«ю.ш.indexPath(for:) шаблона и делегата

В этом примере мы устанавливаем контроллер представления в качестве делегата ячейки. При нажатии на кнопку ячейки запускается вызов соответствующего метода делегата.

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. Использование UITableView«S indexPath(for:)и закрытие для делегирования

Это альтернатива предыдущему примеру, где мы используем замыкание вместо объявления протокола-делегата для обработки нажатия кнопки.

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. Использование UITableViewCell's accessoryTypeи UITableViewDelegate'tableView(_:accessoryButtonTappedForRowWith:)

Если кнопка является UITableViewCell«s стандарт управления аксессуаром, любое нажатие на нее вызовет вызов UITableViewDelegate» с tableView(_:accessoryButtonTappedForRowWith:), что позволяет получить соответствующий путь индекса.

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

Я бы использовал свойство tag, как вы сказали, установив тег следующим образом:

[button setTag:indexPath.row];

затем получить тег внутри buttonPressedAction примерно так:

((UIButton *)sender).tag

Или

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

5
Этот подход полностью нарушен для таблиц с разделами.
Охороб

нет, вы можете просто использовать некоторую простую функцию, чтобы поместить раздел в тег.
ACBurk

2
tagявляется целым числом кажется немного неуклюжим для кодирования / декодирования индексных путей в теги представления.
Охороб

Это правильно, но это решение, хотя не то, которое я бы использовал, если бы у меня были разделы. Все, что я пытался сказать, это то, что это можно сделать с помощью этого метода, что он не сломан. Лучшая, более сложная версия будет определять путь индекса по позиции кнопки внутри UITableView. Однако, поскольку Рин сказал, что у него всего пять ячеек (без разделов), это, вероятно, делает этот метод более сложным, а ваш первоначальный комментарий и всю цепочку комментариев - бессмысленными.
ACBurk

3

Хотя мне нравится способ тегов ... если вы не хотите использовать теги по какой-либо причине, вы можете создать элемент NSArrayготовых кнопок:

NSArray* buttons ;

затем создайте эти кнопки перед рендерингом tableView и вставьте их в массив.

Тогда внутри tableView:cellForRowAtIndexPath:функции вы можете сделать:

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

Тогда в buttonPressedAction:функции вы можете сделать

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

2

ОБРАЩАТЬСЯ С РАЗДЕЛАМИ - я сохранил NSIndexPath в пользовательском UITableViewCell

IN CLKIndexPricesHEADERTableViewCell.xib

В IB Добавьте UIButton в XIB - НЕ добавляйте действие!

Добавить выход @property (сохранить, неатомный) IBOutlet UIButton * buttonIndexSectionClose;

НЕ CTRL + DRAG действие в IB (сделано в коде ниже)

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

В viewForHeaderInSection (также должен работать для cellForRow .... и т. Д., Если в вашей таблице только 1 раздел)

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

... используйте раздел, чтобы получить данные для вашей ячейки

...заполните это

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

ПОЛЬЗОВАТЕЛЬ нажимает кнопку УДАЛИТЬ на заголовке раздела, и это вызывает

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

В этом примере я добавил кнопку «Удалить», поэтому для ее подтверждения должен отображаться UIAlertView.

Я сохраняю раздел и ключ в словарь, хранящий информацию о разделе в иваре в ВК

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

Это немного глючит, потому что indexPath ячейки может измениться, если вы вызываете deleteRowsAtIndexPaths.
Джон Гибб

deleteRowsAtIndexPaths приведет к повторному вызову cellForRowAtIndexPath. Тогда у кнопок будут новые правильные indexPaths.
mmmanishs

1

Это работает и для меня, спасибо @Cocoanut

Я обнаружил, что метод использования суперпредставления суперпредставления для получения ссылки на indexPath ячейки работает отлично. Спасибо iphonedevbook.com (macnsmith) за текст ссылки

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

}

0

Вы можете использовать шаблон тега:

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

Как бы я пометил элементы управления, если бы у меня было несколько элементов управления в одной ячейке?
узда

Я не уверен, что это сработает - если ячейка будет создана для строки № 1, то она получит тег 1. Если она будет удалена для строки № 3, то она все равно будет иметь тег 1, а не 3.
Повторите

думаю, вы правы насчет второго комментария. моя вина. Я думаю, что вашим лучшим решением является создание подкласса UIButton, добавление еще одного или двух ваших собственных свойств, а затем установка / получение их в соответствующих случаях (придерживайтесь тега: 1, который был в вашем коде)
Nir Levy

0

Я что-то упускаю? Разве вы не можете просто использовать отправителя для идентификации кнопки. Отправитель предоставит вам такую ​​информацию:

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

Затем, если вы хотите изменить свойства кнопки, произнесите фоновое изображение, которое вы только что сказали отправителю:

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

Если вам нужен тег, то метод ACBurk подойдет.


1
Они ищут свой «объект», к которому относится кнопка
охороб

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

На самом деле довольно просто:

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

Работает хорошо для меня: P

если вы хотите изменить настройку целевого действия, вы можете включить в метод параметр события, а затем использовать касания этого события, чтобы разрешить координаты касания. Координаты все еще должны быть разрешены в границах сенсорного просмотра, но это может показаться некоторым людям проще.


0

создайте массив nsmutable и поместите все кнопки в этот массив usint [массив addObject: yourButton];

в методе нажатия кнопки

-

 (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

Небольшой вариант ответа Cocoanuts (который помог мне решить эту проблему), когда кнопка находилась в нижнем колонтитуле таблицы (что мешает вам найти «нажатую ячейку»):

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

Я всегда использую теги.

Вам нужно создать подкласс UITableviewCellи обработать нажатие кнопки оттуда.


Я не совсем понимаю, как. Свойство тега задается при создании ячейки - эта ячейка может использоваться повторно для каждой строки с одинаковым идентификатором. Этот тег относится к элементу управления в общей ячейке многократного использования. Как я могу использовать этот тег, чтобы различать кнопки в ячейках, которые были созданы общим способом? Не могли бы вы опубликовать код?
узда

0

Это просто; сделать кастомную ячейку и взять розетку кнопки

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

    cell.yourButton.tag = indexPath.Row;

- (void)buttonPressedAction:(id)sender

изменить идентификатор в вышеуказанном методе на (UIButton *)

Вы можете получить значение, которое нажимается на кнопку, выполнив sender.tag.


0

Создайте подкласс для кнопки для сохранения требуемого значения, возможно, создайте протокол (ControlWithData или что-то еще). Установите значение при добавлении кнопки в ячейку табличного представления. В вашем событии касания проверьте, подчиняется ли отправитель протоколу, и извлеките данные. Обычно я храню ссылку на фактический объект, который отображается в ячейке табличного представления.


0

SWIFT 2 ОБНОВЛЕНИЕ

Вот как узнать, какая кнопка была нажата + отправить данные другому ViewController с этой кнопки, indexPath.rowкак я полагаю, в этом смысл большинства!

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

    }

Для тех, кто использует класс ViewController и добавил tableView, я использую ViewController вместо TableViewController, поэтому я вручную добавил tableView для доступа к нему.

Вот код для передачи данных в другой VC при нажатии этой кнопки и передачи ячейки 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

Обратите внимание, здесь я использую пользовательскую ячейку, этот код отлично работает для меня

 @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

Решение Криса Швердта, но потом в Свифте, сработало для меня:

@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

Эта проблема состоит из двух частей:

1) Получение индекса пути UITableViewCellкоторого содержит нажатыеUIButton

Есть несколько предложений, таких как:

  • Обновление UIButton«S tagв cellForRowAtIndexPath:методе с использованием индекса путей в rowзначение. Это не очень хорошее решение, так как требует tagпостоянного обновления и не работает с табличными представлениями с несколькими разделами.

  • Добавление NSIndexPathсвойства в пользовательской ячейке и обновить его вместо UIButton«с tagв cellForRowAtIndexPath:методе. Это решает проблему с несколькими разделами, но все же не очень хорошо, поскольку требует постоянного обновления.

  • Сохранение слабой ссылки на родителя UITableViewв пользовательской ячейке при ее создании и использование indexPathForCell:метода для получения пути индекса. Кажется, немного лучше, нет необходимости что-либо обновлять в cellForRowAtIndexPath:методе, но все равно требуется установка слабой ссылки при создании пользовательской ячейки.

  • Использование superViewсвойства ячейки для получения ссылки на родителя UITableView. Нет необходимости добавлять какие-либо свойства в пользовательскую ячейку, и нет необходимости устанавливать / обновлять что-либо при создании / позже. Но ячейка superViewзависит от деталей реализации iOS. Так что его нельзя использовать напрямую.

Но это может быть достигнуто с помощью простого цикла, поскольку мы уверены, что рассматриваемая ячейка должна быть в UITableView:

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

Таким образом, эти предложения могут быть объединены в простой и безопасный пользовательский метод ячейки для получения пути индекса:

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

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

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

Отныне этот метод можно использовать для определения того, какая из UIButtonних нажата.

2) Информирование других сторон о событии нажатия кнопки

После того, как внутренне узнают, какая UIButtonнажата в какой пользовательской ячейке с точным индексным путем, эта информация должна быть отправлена ​​другим сторонам (наиболее вероятно, контроллер представления, обрабатывающий UITableView). Таким образом, это событие нажатия кнопки может быть обработано на уровне абстракции и логики, аналогичном didSelectRowAtIndexPath:методу делегата UITableView.

Для этого можно использовать два подхода:

а) Делегирование: пользовательская ячейка может иметь delegateсвойство и определять протокол. Когда кнопка нажата, она просто выполняет методы делегата для своего delegateсвойства. Но это delegateсвойство должно быть установлено для каждой пользовательской ячейки при их создании. В качестве альтернативы, пользовательская ячейка может выбрать выполнение своих методов делегата и для родительского табличного представления delegate.

b) Центр уведомлений: настраиваемые ячейки могут определять имя настраиваемого уведомления и публиковать это уведомление с указанием пути индекса и представления родительской таблицы в userInfoобъекте. Не нужно ничего устанавливать для каждой ячейки, достаточно просто добавить наблюдателя для уведомления пользовательской ячейки.


0

Я использую решение, которое подкласс, UIButtonи я подумал, что я должен просто поделиться им здесь, коды в Swift:

class ButtonWithIndexPath : UIButton {
    var indexPath:IndexPath?
}

Затем не забудьте обновить его indexPath в 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
}

Поэтому, отвечая на событие кнопки, вы можете использовать его как

func cellButtonPressed(_ sender:UIButton) {
    if sender is ButtonWithIndexPath {
        let button = sender as! ButtonWithIndexPath
        print(button.indexPath)
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.