带有UITextField的UITableViewCell失去了选择UITableView行的能力?

16

我已经几乎完成了一个包含UITextFieldUITableViewCell的实现。而不是通过CGRectMakeUITableViewCell.contentView来完成,我采用了更简单的方法:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"Cell"];
    [cell setSelectionStyle:UITableViewCellSelectionStyleBlue];
    amountField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 190, 30)];
    amountField.placeholder = @"Enter amount";
    amountField.keyboardType = UIKeyboardTypeDecimalPad;
    amountField.textAlignment = UITextAlignmentRight;
    amountField.clearButtonMode = UITextFieldViewModeNever; 
    [amountField setDelegate:self];

    [[cell textLabel] setText:@"Amount"];
    [cell addSubview:amountField];
    return cell;
}

然后我还实现了didSelectRow方法,将textField释放以显示其他字段的输入视图。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    ...
    [amountField resignFirstResponder];
    ...
}

这段代码能够正常工作,但是当选中表格中的其他行时,整个单元格会被选中并变成蓝色,而我的UITextField所在的那一行并没有被选中,也就是说我可以输入文本但是该行并不被选中。 我已经测试过,发现问题出在以下这一行代码:

[cell addSubview:amountField];

似乎这破坏了可选择单元格的行为,即使将其添加到[cell contentView]也无法解决。我错过了什么吗?

5个回答

46
如果文本字段的userInteractionEnabled设置为YES并且占据了整个单元格,你无法使单元格响应触摸。为了让单元格响应触摸,需要将文本字段的userInteractionEnabled设置为NO编辑:如果想在选中单元格时使文本字段可编辑,则需在didSelectRowAtIndexPath:方法中添加以下代码:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    // get the reference to the text field
    [textField setUserInteractionEnabled:YES];
    [textField becomeFirstResponder];
}

如果您设置 userInteractionEnabled = NO;,那么这不会使 UITextField 无法编辑吗? - user814037
显然不是这样的:我遵循了EmptyStack的建议,在DidSelectRow方法的开头设置了[amountField setUserInteractionEnabled:NO];,然后只有在所选行是文本字段的行时才设置[amountField setUserInteractionEnabled:YES]。选择现在可以正常进行,但我必须在行上点击两次才能实际输入文本... - Fabrizio Prosperi
@micpringle,当然。由于文本字段填充单元格,因此他无法同时使文本字段和单元格响应触摸。 - EmptyStack
1
@Fabrizio Prosperi,您不需要在单元格上敲两次。您可以在didSelectRowAtIndexPath:方法中,在[amountField setUserInteractionEnabled:YES]行后调用[amountField becomeFirstResponder] - EmptyStack
谢谢EmptyStack,你的解决方案很简单,而且在这里运行得非常顺畅! - Fabrizio Prosperi
显示剩余3条评论

6

您添加子视图并没有破坏任何东西,而是UITextField在UITableViewCell之前捕获了触摸。您可以通过在UITableViewCell的边界内但在UITextField外部轻按来测试此功能,您会发现它仍然像您期望的那样选择。

为了解决这个问题,您可以创建一个UITextField的子类,并添加一个UITableView属性。在实例化UITextField并将其添加到单元格时设置该属性。

amountField.tableView = tableView;

那么你需要在你的子类中重写 becomeFirstResponder 方法,并在该方法中获取带有 UITextField 的单元格的行,然后手动选择它。

- (BOOL)becomeFirstResponder
{
    // Get the rect of the UITextView in the UITableView's coordinate system
    CGRect position = [self convertRect:self.frame toView:self.tableView];
    // Ask the UITableView for all the rows in that rect, in this case it should be 1
    NSArray *indexPaths = [self.tableView indexPathsForRowsInRect:position];
    // Then manually select it
    [self.tableView selectRowAtIndexPath:[indexPaths objectAtIndex:0] animated:YES scrollPosition:UITableViewScrollPositionNone];
    return YES;
}

这也可以实现,但需要大量的额外代码,而像EmptyStack建议的那样将文本字段userInteractionEnabled=NO设置为几行代码...至少在我发现其他问题之前是这样的 :) 虽然谢谢你的帮助,它让问题更清晰了。 - Fabrizio Prosperi
这不是一个保留循环吗?tableView -> visibleCells -> amountField -> tableView - jakenberg
@jakenberg,对于amountField上的tableView属性,应该指定为weak,以避免潜在的保留循环。 - Ilias Karim
不要忘记调用 [super becomeFirstResponder]; - John D.

1

首先,您还没有使用可重用单元格。您提供的代码将导致大量内存消耗。

其次,您可以通过触摸文本框以外的区域来选择行。

您的问题的一个解决方案是

在您的textField Delegate中

UITableViewCell *cell = (UITableViewCell *)[textField superView];
cell.selected=YES; //I think this will call the didSelectRowATIndex;

我不确定这会起作用。但值得一试。


除了这个表格只有4个单元格因为它是一个不错的输入掩码之外,所以内存不应该是个大问题,但是你为什么说我没有使用可重用的单元格呢?我以为我已经用了。我现在要试一下你的解决方案。谢谢。 - Fabrizio Prosperi
我认为你误解了可重用的概念。请看这里http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html...无论如何,4个单元格是可以的,但如果一行超过10个,则会出现问题。 - ipraba
我可能误解了,但是当所有单元格内部都有不同的控件时,重用单元格不应该被避免吗?无论如何,感谢提供链接 :) - Fabrizio Prosperi
我也尝试了你的解决方案,实现在textFieldShouldBeginEditing中,它可以工作,但需要其他代码来在选择其他单元格时取消选择该单元格。无论如何,这是一个很好的提示。谢谢。 - Fabrizio Prosperi

1
首先,在cellForRowAtIndexPath中,你需要使用reuseIdentifier。如果你不使用reuseIdentifier,那么当你上下滚动时,它会一直分配新的单元格和新的文本字段,因此你需要加入cell==nil的条件判断。修订后的代码如下:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  static NSString *reuseIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (cell==nil) {
    cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier] autorelease];

     UITextField *amountField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 190, 30)];
     amountField.placeholder = @"Enter amount";
     amountField.keyboardType = UIKeyboardTypeDecimalPad;
     amountField.textAlignment = UITextAlignmentRight;
     amountField.clearButtonMode = UITextFieldViewModeNever; 
     [amountField setDelegate:self];
     [cell.contentView addSubview:amountField];
}
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[[cell textLabel] setText:@"Amount"];

return cell;
}

在didSelect代理方法中,您可以这样做。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  [prevField resignFirstResponder];
  UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
  UIView *view = [[cell.contentView subviews] lastObject];
  if([view isKindOfClass:[UITextField class]]{
     currentField = (UITextField*)view;
  }
  [currentField becomeFirstResponder];
  prevField = currentField;
}

各位,我很感激你们教授Cocoa最佳实践的努力,但是我尝试只放置相关代码,amountField并不是每次都被实例化,而只在视图加载时进行,检查!cell是否存在,但我省略了因为与我的问题无关,我的表格不滚动,因为它只有4行。不过,你们的其余评论非常有帮助。谢谢! - Fabrizio Prosperi

1

在单元格中为索引路径所在的行添加下面这行代码,代码已用粗体标出:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"Cell"];
    [cell setSelectionStyle:UITableViewCellSelectionStyleBlue];

    amountField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 190, 30)];
    amountField.placeholder = @"Enter amount";
    amountField.keyboardType = UIKeyboardTypeDecimalPad;
    amountField.textAlignment = UITextAlignmentRight;
    amountField.clearButtonMode = UITextFieldViewModeNever; 

    [amountField setDelegate:self];
    [[cell textLabel] setText:@"Amount"];

    **for (UIView *cellSubViews in cell.subviews) {
            cellSubViews.userInteractionEnabled = NO;
    }**

    [cell addSubview:amountField];
    return cell;
}

尝试这个,它会起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接