Визначення, який UIButton був натиснутий у UITableView


212

У мене є UITableView5 UITableViewCells. Кожна комірка містить a, 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
Для першого рішення вам потрібно буде схопити [[кнопковий нагляд] нагляд], оскільки перший виклик нагляду надасть вам contentView, а нарешті другий дасть вам UITableViewCell. Друге рішення не працює добре, якщо ви додаєте / видаляєте комірки, оскільки він недійсний індекс рядка. Тому я пішов із першим рішенням, як було викладено, і воно спрацювало ідеально.
рейдфайв

3
Це надійно виділить клітинку, якій належить кнопка: UIView * view = button; while (! [переглянути isKindOfClass: [клас UITableViewCell]]) {view = [переглянути перегляд]}
Jacob Lyles

1
Існує пастка при використанні: [кнопка addTarget: self action: @selector (checkButtonTapped :) forControlEvents: UIControlEventTouchUpInside]; тому що addTarget: action: forControlEvents: додасть кілька дублюваних цілей та дій під час прокрутки таблиці, вона не видалить попередні цілі та дії, тому метод checkButtonTapped: буде викликаний багато разів при натисканні кнопки. Краще видаліть ціль і дії, перш ніж додавати їх
смуга

48

Я знайшов метод використання нагляду супервизора, щоб отримати посилання на індекс. Дякуємо 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 це ще один посилення нагляду. наприклад, [[супервідвід відправника] нагляд] супервигляд];
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
У цьому прикладі незрозуміло, звідки ви отримуєте об’єкт 'подія'.
Нік Людлам

Це рішення, з яким я пішов. Використання тегів непередбачувано при додаванні / видаленні рядків, оскільки їх індекси змінюються. Також,
рейдфайв

@NickLudlam: ймовірно , ім'я методу не buttonPressedAction:тільки buttonPressedAction:forEvent:.
КПМ

5

Як щодо надсилання такої інформації, як NSIndexPathу UIButtonвикористанні ін'єкції часу виконання.

1) Вам потрібно виконувати час імпорту

2) додати статичну константу

3) додати NSIndexPathдо кнопки під час виконання, використовуючи:

(void) setMetaData: (id) мета зObject: (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

Щоб відповісти (@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' 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. Використання 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's 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' stableView(_:accessoryButtonTappedForRowWith:)

Якщо ваша кнопка - це UITableViewCellстандартний елемент управління аксесуаром, будь-яке натискання на неї викликає дзвінок до UITableViewDelegates 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

Я б використав властивість тегу, як ви сказали, встановивши тег так:

[button setTag:indexPath.row];

потім отримуємо тег всередині кнопкиPressAction так:

((UIButton *)sender).tag

Або

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

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

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

2
tag- ціле число. видається трохи незграбним кодування / декодування шляхів індексів у теги подання.
оххороб

Це правильно, але це рішення, хоч і не таке, яке я використовував би, якби мав розділи. Я лише намагався сказати, що це можна зробити за допомогою цього методу, що він не був порушений. Краща, більш складна версія визначала б шлях покажчика з положення кнопки всередині UITableView. Однак, оскільки Rein сказав, що у нього є лише п’ять комірок (без розділів), це, ймовірно, робить цей метод складним, а ваш початковий коментар і весь цей потік коментарів безглуздий.
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

В CLKIndexPricesHEADERTableViewCell.xib

В ІБ Додати UIButton до XIB - НЕ ДОПУСКАЙТЕ дії!

Додати outlet @property (зберегти, неатомічний) IBOutlet UIButton * buttonIndexSectionClose;

НЕ CTRL + DRAG дія в IB (зроблено в коді нижче)

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

З оглядуForHeaderInSection (також повинен працювати для 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;
}

Користувач натискає кнопку DELETE на заголовку розділу, і це викликає

- (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, щоб підтвердити її

Я зберігаю розділ і ключ до словника, де зберігається інформація про секцію в ivar у ВК

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

Це злегка баггі, тому що індекс-шлях комірки може змінюватися, якщо ви викликаєте deleteRowsAtIndexPaths.
Джон Гібб

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

1

Для мене це також працює, Дякую @Cocoanut

Я знайшов метод використання нагляду супервизора, щоб отримати посилання на індекс. Дякуємо 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 у вас був код)
Нір Леві

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

Незначна зміна відповіді на какао-горіхи (що допомогло мені вирішити це), коли кнопка знаходилась у нижньому колонтитулі таблиці (що заважає вам знайти "натиснуту клітинку":

-(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 для доступу до нього.

Ось код для передачі даних на інший ВК при натисканні на цю кнопку та передачі комірки 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.