为TableView中的所有单元格设置标签的正确方法

我正在使用一个tableView中的button,当我按下时,我得到的indexPath.row 。 但是,当单元格可以在屏幕上显示而不scroll时,它才能正常工作。

一旦tableView可以滚动,我滚动tableview, indexPath.row返回是一个错误的值,我注意到,最初设置20个对象,例如Check只是打印9次没有20。

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; lBtnWithAction = [[UIButton alloc] initWithFrame:CGRectMake(liLight1Xcord + 23, 10, liLight1Width + 5, liLight1Height + 25)]; lBtnWithAction.tag = ROW_BUTTON_ACTION; lBtnWithAction.titleLabel.font = luiFontCheckmark; lBtnWithAction.tintColor = [UIColor blackColor]; lBtnWithAction.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; [cell.contentView addSubview:lBtnWithAction]; } else { lBtnWithAction = (UIButton *)[cell.contentView viewWithTag:ROW_BUTTON_ACTION]; } //Set the tag lBtnWithAction.tag = indexPath.row; //Add the click event to the button inside a row [lBtnWithAction addTarget:self action:@selector(rowButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; //This is printed just 9 times (the the number of cells that are initially displayed in the screen with no scroll), when scrolling the other ones are printed NSLog(@"Check: %li", (long)indexPath.row); return cell; } 

要点击索引来做些事情:

 -(void)rowButtonClicked:(UIButton*)sender { NSLog(@"Pressed: %li", (long)sender.tag); } 

Constants.h

 #define ROW_BUTTON_ACTION 9 

什么是正确的方式来获得indexPath.row里面rowButtonClicked或设置一个标签,当我有很多的单元格在我的tableView

我对这类问题的解决方法是不要以这种方式使用标签。 这是一个完全滥用标签(在我看来),并可能导致麻烦(正如你所发现的),因为细胞被重复使用。

通常情况下,解决的问题是:单元中的一块界面与用户交互(例如点击一个button),现在我们想要知道该单元当前对应的什么 ,以便我们可以对其进行响应到相应的数据模型。

我在我的应用程序中解决这个问题的方式是,当button被轻敲或任何我收到一个控制事件或委托事件,从这个界面(button或任何)走向视图层次结构,直到我来到单元格,然后调用表视图的indexPath(for:) ,它将一个单元格并返回相应的索引path。 控件事件或委托事件始终将接口对象作为参数包含在内,因此很容易从该对象到单元格以及从那里到该行。

因此,例如:

 UIView* v = // sender, the interface object do { v = v.superview; } while (![v isKindOfClass: [UITableViewCell class]]); UITableViewCell* cell = (UITableViewCell*)v; NSIndexPath* ip = [self.tableView indexPathForCell:cell]; // and now we know the row (ip.row) 

[ 注:一种可能的替代方法是使用一个自定义的单元格子类,其中有一个特殊的属性,将行存储在cellForRowAt 但是在我看来,这完全没有必要,因为indexPath(for:)给了你完全相同的信息! 另一方面,对于页眉/页脚没有indexPath(for:) ,所以在这种情况下,我使用一个自定义的子类来存储段号,如本例所示 (参见viewForHeaderInSection的实现)。]

我同意@matt,这不是一个很好的使用标签,但稍微不同意他的解决scheme。 除了直到find一个单元格之前,button的高级视图,我更喜欢获取button的原点,将其转换为表视图坐标,然后向表视图索取包含这些坐标的单元格的indexPath。

我希望苹果将添加一个函数indexPathForView(_:)到UITableView。 这是一个普遍的需求,而且易于实施。 为此,下面是对UITableView的一个简单的扩展,它可以让你在表视图中查询位于tableView的单元格内的任何视图的indexPath。

在Objective-C和Swift中,下面是扩展的关键代码。 GitHub上有一个名为TableViewExtension-Obj-C的工作项目,演示了下面的表视图扩展的用法。

编辑

在Objective-C中:

头文件UITableView_indexPathForView.h:

 #import <UIKit/UIKit.h> @interface UIView (indexPathForView) - (NSIndexPath *) indexPathForView: (UIView *) view; @end 

UITableView_indexPathForView.m文件:

 #import "UITableView_indexPathForView.h" @implementation UITableView (UITableView_indexPathForView) - (NSIndexPath *) indexPathForView: (UIView *) view { CGPoint origin = view.bounds.origin; CGPoint viewOrigin = [self convertPoint: origin fromView: view]; return [self indexPathForRowAtPoint: viewOrigin]; } 

和button上的IBAction:

 - (void) buttonTapped: (UIButton *) sender { NSIndexPath *indexPath = [self.tableView indexPathForView: sender]; NSLog(@"Button tapped at indexpPath [%ld-%ld]", (long)indexPath.section, (long)indexPath.row); } 

在Swift中:

 import UIKit public extension UITableView { func indexPathForView(_ view: UIView) -> IndexPath? { let origin = view.bounds.origin let viewOrigin = self.convert(origin, from: view) let indexPath = self.indexPathForRow(at: viewOrigin) return indexPath } } 

我把它作为一个文件“UITableView + indexPathForView”添加到一个testing项目,以确保我得到的一切正确。 然后在IBAction中的一个单元格内的button:

 func buttonTapped(_ button: UIButton) { let indexPath = self.tableView.indexPathForView(button) print("Button tapped at indexPath \(indexPath)") } 

我在任何 UIView上做了扩展,而不仅仅是button,所以它更通用。

关于这个扩展的indexPathForView(_:)是你可以把它放到任何项目中,并且把新的indexPathForView(_:)函数添加到所有的表格视图中,而根本不需要改变你的其他代码。

您正在遇到细胞重复使用的问题。

当你为视图创build一个button时,你为它设置一个标签,但是你重写这个标签来设置它的行号。

当单元格被重用时,由于行号更长ROW_BUTTON_ACTION,因此不会将标记重置为正确的行号,从而导致错误。

使用标签从视图中获取信息几乎总是一个坏主意,而且非常脆弱,正如您在这里所看到的。

正如马特所说,走等级制是一个更好的主意。

而且,你的方法不需要这样写。 如果您创build自己的自定义单元格,则不需要用于创build和添加button和标签的代码,您可以在xib,故事板或甚至是类中的代码中执行该代码。 此外,如果使用采用索引path的出队方法,则将始终获得循环单元格或新创build的单元格,因此不需要检查返回的单元格是否为零。