检测在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)获取包含按下的UIButton
的UITableViewCell
的索引path
有一些build议,如:
-
使用索引path的
row
值在cellForRowAtIndexPath:
方法中更新UIButton
的tag
。 这不是一个好的解决scheme,因为它需要连续更新tag
,并且不能用多于一个部分的表格视图。 -
将一个
NSIndexPath
属性添加到自定义单元格,并在cellForRowAtIndexPath:
方法中更新它,而不是UIButton
的tag
。 这解决了多个部分的问题,但仍然不好,因为它总是需要更新。 -
在创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) } }