检测在UITableView中按下了哪个UIButton


212

我有一个UITableView5 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;
}

需要注意的重要一点是,由于单元可能会出队,因此我无法在单元的创建中设置标签。感觉很脏。肯定有更好的办法。


使用您的标签解决方案没有任何问题。单元被重用,因此按照此处的方式将标签设置为行索引是有意义的。与将触摸位置转换为行索引相比,我发现这是一种更为优雅的解决方案,如下所示。
Erik van der Neut 2015年

Answers:


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中,因此您必须与创建单元格时所做的保持一致。尽管这种方法看起来并不十分优雅,但我必须承认
弗拉基米尔(Vladimir)2010年

1
对于第一个解决方案,您将需要获取[[button superview]超级视图],因为第一个超级视图调用将为您提供contentView,最后一个将为您提供UITableViewCell。如果要添加/删除单元格,第二种解决方案将无法正常工作,因为这会使行索引无效。因此,我采用了第一个概述的解决方案,并且效果很好。
raidfive

3
这将可靠地挑选出拥有按钮的单元格:UIView * view = button; while(![view isKindOfClass:[UITableViewCell class]]){view = [view superview]}
Jacob Lyles 2010年

1
使用时有一个陷阱:[button addTarget:self操作:@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的直接子代,因此我需要取出Cocoanut代码中的“超级视图”之一,然后它才起作用。
乔恩·施耐德

29
这非常非常错误,并且在较新版本的OS中已被打破。不要走自己不拥有的Superview树。
肯里克(Kenrik),

2
这在iOS 6下为我工作,但在iOS 7中已损坏。看来@KenrikMarch有一个正确的观点!
乔恩·施耐德

3
在iOS 7中,超级视图又增加了1个。例如[[[sender superview] superview] superView];
CW0007007 2014年

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

易于使用。此外,很容易将其翻译为Swift3。您是最好的:)
Francisco Romero

将此翻译为下面的Swift。我能找到的最简单的解决方案。谢谢克里斯!
罗格·休伊斯曼斯

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
在此示例中,您不清楚从何处获取“事件”对象。
尼克·卢德兰

这是我的解决方案。添加/删除行时,使用标签是不可预测的,因为它们的索引会更改。此外,
raidfive

@NickLudlam:方法名称可能不是buttonPressedAction:but buttonPressedAction:forEvent:
KPM 2012年

5

如何发送的信息,如NSIndexPathUIButton使用运行时注入。

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 使用UIViewconvert(_:to:)UITableViewindexPathForRow(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。使用UIViewconvert(_:to:)UITableViewindexPathForRow(at:)(替代)

这是前面的示例的替代方法,在该示例中,我们将参数传递nil给。这样,如果第一个响应者未执行该操作,它将被发送到响应者链中的下一个响应者,直到找到正确的实现为止。targetaddTarget(_: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。使用UITableViewindexPath(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。使用UITableViewindexPath(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。使用UITableViewCellaccessoryTypeUITableViewDelegatetableView(_:accessoryButtonTappedForRowWith:)

如果您的按钮是一个UITableViewCell的标准附件控制,它的任何抽头将触发调用UITableViewDelegatetableView(_: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
对于带有节的表,这种方法完全不可用。
ohhorob

不,您也可以使用一些简单的功能将该部分也放入标签中。
ACBurk 2010年

2
tag是一个整数。将索引路径编码/解码到视图标签中似乎有点笨拙。
ohhorob

没错,但这是一个解决方案,但如果有部分内容,我就不会使用。我只是想说的是,可以使用此方法完成此操作,并且不会损坏它。更好,更复杂的版本将根据UITableView内部按钮的位置确定indexpath。但是,由于rein表示他只有五个单元格(无节),因此可能会使该方法变得过于复杂,并且您的初始注释和整个注释线程毫无意义。
ACBurk 2010年

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中

在IB中将UIButton添加到XIB-不要添加操作!

添加插座@属性(保留,非原子)IBOutlet UIButton * buttonIndexSectionClose;

请勿在IB中CTRL + DRAG动作(在下面的代码中完成)

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

USER按下Section标头上的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__);
    }
}

在此示例中,我添加了一个Delete按钮,因此应显示UIAlertView进行确认

我将部分和密钥存储在字典中,该字典将有关该部分的信息存储在VC中的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,则单元的indexPath可以更改。
John Gibb 2014年

deleteRowsAtIndexPaths将导致cellForRowAtIndexPath再次被调用。然后,按钮将具有新的正确indexPaths。
mmmanishs 2014年

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的类,添加自己的另一个或两个属性,然后在适当的情况下设置/获取它们(坚持使用代码中的tag: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
他们正在寻找与按钮相关的“对象”
ohhorob

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 [array 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

将上述方法中的id更改为 (UIButton *)

您可以通过执行sender.tag来获取正在点击哪个按钮的值。


0

对按钮进行子类化以存储所需的值,也许可以创建协议(ControlWithData或类似的东西)。将按钮添加到表格视图单元格时设置值。在您的润饰事件中,查看发件人是否遵守协议并提取数据。我通常存储对在表视图单元格上呈现的实际对象的引用。


0

SWIFT 2更新

这是找出被点击的按钮的方法,indexPath.row以及从该按钮发送数据到另一个ViewController的方法,因为我假设这是大多数问题的关键!

@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

Chris Schwerdt的解决方案,但后来在Swift中为我工作:

@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

有一些建议,例如:

  • 更新UIButtontagcellForRowAtIndexPath:使用索引路径的方法row的值。这不是一个好的解决方案,因为它需要tag连续更新,并且不适用于具有多个部分的表视图。

  • NSIndexPath属性添加到自定义单元格并更新它,而不是UIButtontagin cellForRowAtIndexPath:方法。这解决了多节问题,但仍然不好,因为它需要始终进行更新。

  • UITableView在创建自定义单元格并使用indexPathForCell:方法获取索引路径时,请使其对父级保持微弱的引用。似乎好一点,不需要更新cellForRowAtIndexPath:方法中的任何内容,但是在创建自定义单元格时仍然需要设置一个弱引用。

  • 使用cell的superView属性获取对parent的引用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委托方法的抽象和逻辑级别上处理此按钮单击事件。

可以使用两种方法:

a)委派:自定义单元格可以具有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)
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.