检测在UITableView中按下了哪个UIButton

我有一个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:方法,我怎么知道哪个button被按下。 我已经考虑过使用标签,但我不确定这是最好的路线。 我想能够以某种方式将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; } 

重要的是要注意的是,我不能在创build单元格时设置标签,因为单元格可能会被取消。 感觉很肮脏。 一定会有更好的办法。

在Apple的附件示例中,使用以下方法:

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

然后在触摸处理程序触摸坐标检索和索引path从该坐标计算:

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

我find了使用superview的superview获得对单元格indexPath的引用完美工作的方法。 感谢iphonedevbook.com(macnsmith)的提示链接文本

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

这是我怎么做的。 简单而简洁:

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

在其他地方find了一个很好的解决scheme,不会弄乱button上的标签:

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

如何使用运行时注入在UIButton发送像NSIndexPath这样的信息。

1)您需要导入的运行时

2)添加静态常量

3)在运行时添加NSIndexPath到你的button使用:

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

4)在button上按下获取元数据:

(ID)的元数据:(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 } 

做(@Vladimir)的答案是Swift:

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

尽pipe检查indexPath != nil给了我手指…“NSIndexPath不是NSString的子types”

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

我会像你说的那样使用标签属性,像这样设置标签:

 [button setTag:indexPath.row]; 

然后得到buttonPressedAction里面的标签就像这样:

 ((UIButton *)sender).tag 

要么

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

虽然我喜欢标签的方式…如果你不想因为任何原因使用标签,你可以创build一个预制button的NSArray成员:

 NSArray* buttons ; 

然后在渲染tableView之前创build这些button并将它们推入数组中。

然后在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 } 

处理部分 – 我将NSIndexPath存储在一个自定义的UITableViewCell中

在CLKIndexPricesHEADERTableViewCell.xib中

IN IB添加UIButton到XIB – 不要添加动作!

添加sockets@property(保留,非primefaces)IBOutlet UIButton * buttonIndexSectionClose;

不要在IB中按CTRL +拖动操作(在下面的代码中完成)

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

用户按部分标题上的DELETEbutton,并调用

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

在这个例子中,我添加了一个删除button,所以应该显示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); } } 
 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; //:) } 

它适用于我,也谢谢@Cocoanut

我find了使用superview的superview获得对单元格indexPath的引用完美工作的方法。 感谢iphonedevbook.com(macnsmith)的提示链接文本

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

你可以使用标签模式:

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

我错过了什么吗? 你不能只使用发件人来识别button。 发件人会给你这样的信息:

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

然后,如果你想改变button的属性,说你刚才告诉发件人的背景图片:

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

如果你需要标签,那么ACBurk的方法就好了。

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

如果要调整目标动作设置,可以在方法中包含事件参数,然后使用该事件的触摸来parsing触摸的坐标。 触摸视图边界上的坐标仍然需要parsing,但对于某些人来说可能更容易。

创build一个nsmutable数组,并把所有的button放在该数组usint [array addObject:yourButton];

在button按下的方法

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

Cocoanuts的一个小小的变化回答(帮助我解决了这个问题),当button在桌子的底部(这样可以防止你find“被点击的单元格”:

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

我总是使用标签。

你需要UITableviewCell并从那里处理button。

这很简单; 做一个自定义的单元格,并采取button的出口

 - (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来获取正在点击哪个button的值。

子类button来存储所需的值,也许创build一个协议(ControlWithData或者其他)。 在将button添加到表视图单元格时设置值。 在您的触摸事件中,查看发件人是否遵守协议并提取数据。 我通常存储在表视图单元格上呈现的实际对象的引用。

SWIFT 2更新

下面是如何找出哪个button被点击+从该button的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以访问它。

这是当点击该button并传递单元格的 indexPath.row时,将数据传递给另一个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) } 

注意这里我使用自定义单元格这个代码完全为我工作

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

Chris Schwerdt的解决scheme,但后来在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) } 

这个问题有两个部分:

1)获取包含按下​​的UIButtonUITableViewCell的索引path

有一些build议,如:

  • 使用索引path的row值在cellForRowAtIndexPath:方法中更新UIButtontag 。 这不是一个好的解决scheme,因为它需要连续更新tag ,并且不能用多于一个部分的表格视图。

  • 将一个NSIndexPath属性添加到自定义单元格,并在cellForRowAtIndexPath:方法中更新它,而不是UIButtontag 。 这解决了多个部分的问题,但仍然不好,因为它总是需要更新。

  • 在创build它时使用自定义单元格中的父UITableView ,并使用indexPathForCell:方法获取索引path。 看起来好一点,不需要更新cellForRowAtIndexPath:方法中的任何东西,但仍需要在创build自定义单元格时设置弱引用。

  • 使用单元格的superView属性来获得对父UITableView的引用。 无需向自定义单元添加任何属性,也不需要在创build/更新时设置/更新任何内容。 但是cell的superView取决于iOS的实现细节。 所以不能直接使用。

但是这可以通过一个简单的循环来实现,因为我们确信有问题的单元必须位于UITableView中:

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

因此,这些build议可以组合成一个简单而安全的定制单元格方法来获取索引path:

 - (NSIndexPath *)indexPath { UIView* view = self; while (view && ![view isKindOfClass:UITableView.class]) view = view.superview; return [(UITableView*)view indexPathForCell:self]; } 

从现在起,这个方法可以用来检测哪个UIButton被按下了。

2)通知其他方关于button按下事件

在内部知道哪个UIButton被按下了哪个定制单元格的确切索引path后,这个信息需要发送给其他各方(最有可能是处理UITableView的视图控制器)。 所以,这个button点击事件可以在一个类似的抽象和逻辑层面上处理,以实现UITableView委托的方法:SelectSelectRowAtIndexPath。

可以使用两种方法:

a)委托:自定义单元格可以具有delegate属性并可以定义一个协议。 当按下button时,它只是执行它委托方法的delegate属性。 但是这个delegate属性需要在创build时为每个自定义单元格设置。 作为替代,自定义单元格可以select在其父表视图的delegate上执行委托方法。

b)通知中心:自定义单元格可以定义一个自定义的通知名称,并将该通知与userInfo对象中提供的索引path和父表视图信息一起发布。 不需要为每个单元格设置任何内容,只需添加一个观察者来定制单元格的通知即可。

我使用的子类UIButton的解决scheme,我想我应该在这里分享它,在Swift代码:

 class ButtonWithIndexPath : UIButton { var indexPath:IndexPath? } 

然后记得在cellForRow(at:)更新它的indexPath

 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 } 

所以当响应button的事件,你可以使用它

 func cellButtonPressed(_ sender:UIButton) { if sender is ButtonWithIndexPath { let button = sender as! ButtonWithIndexPath print(button.indexPath) } }