在iOS中,如何在两个单元格中的文本字段之间切换?

在iOS中,如何在两个单元格中的文本字段之间切换?,ios,objective-c,uitableview,uitextfield,Ios,Objective C,Uitableview,Uitextfield,我有一个应用程序使用自定义单元格的TableView,每个单元格都包含一个文本字段。当用户点击单元格时,我想将相应的文本字段置于编辑模式。如果另一个单元格的文本字段已经处于编辑模式,我需要让它退出FirstResponder,这样我可以保存它的数据,然后使新单元格的文本字段成为FirstResponder。但当我在新单元格的文本字段上调用becomeFirstResponder时,它返回一个零表示失败。这似乎是一种混乱的处理方式,但我还没有找到更好的方法 以下是我的TableViewContro

我有一个应用程序使用自定义单元格的TableView,每个单元格都包含一个文本字段。当用户点击单元格时,我想将相应的文本字段置于编辑模式。如果另一个单元格的文本字段已经处于编辑模式,我需要让它退出FirstResponder,这样我可以保存它的数据,然后使新单元格的文本字段成为FirstResponder。但当我在新单元格的文本字段上调用becomeFirstResponder时,它返回一个零表示失败。这似乎是一种混乱的处理方式,但我还没有找到更好的方法

以下是我的TableViewController文件的全文,其中有许多杂乱无章的内容:

//  TEST_TVC.h
//  TVC_Test
#import <UIKit/UIKit.h>
@interface TEST_TVC : UITableViewController <UITextFieldDelegate>
@end


//  TEST_TVC.m
//  TVC_Test
#import "TEST_TVC.h"
@interface TEST_TVC ()

@property (strong, nonatomic) NSMutableArray * TestData;
@property (strong, nonatomic) UITextField * fieldBeingEdited;
@property (assign, nonatomic) NSInteger cellIndex;
@property (strong, nonatomic) UITableViewCell * cellContainingField;

@end

@implementation TEST_TVC

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Init data for testing the TableView
    self.TestData = [NSMutableArray array];
    for (int i=0; i<25; i++) {
        NSString * title = [NSString stringWithFormat:@"Field %d", i];
        NSArray * data = [NSArray arrayWithObjects:title, @"*", nil];
        [self.TestData addObject:data];
    }
    self.fieldBeingEdited = NULL;
    self.cellContainingField = NULL;
    self.cellIndex = -1;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.TestData.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Configure the cell...
    NSString *CellIdentifier = @"TVCell";
    NSString * titleText;
    NSString * detailText;
    NSString * detailPlaceholder = @"none";

    // Get the data for this row
    int rowNumber = indexPath.row;
    NSArray * cellData = self.TestData[rowNumber];
    titleText = cellData[0];
    detailText = cellData[1];
    //NSLog(@"cellForRow..., entered row=%d, titleText=%@, detailText=%@", rowNumber, titleText, detailText);

    // Retrieve a pre-built cell to fill in the blanks
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    UILabel * cellTitle = (UILabel *)[cell viewWithTag:98];
    cellTitle.text = titleText;
    UITextField * cellDetail = (UITextField *)[cell viewWithTag:99];
    NSLog(@"cellForRow.. reusing cell with Title=%@ and Detail=%@", cellTitle.text, cellDetail.text);
    NSLog(@"cellForRow.. intended Title=%@, intended Detail=%@", titleText, detailText);
    if (cellDetail == NULL) {
        NSLog(@"cellForRow..; cellDetail = NULL *******************************************");
    }

    // Set default configuration of the cellData UITextField here, and make exceptions later
    cellDetail.placeholder = @"";
    cellDetail.text = @"";
    cellDetail.borderStyle = UITextBorderStyleRoundedRect;
    cellDetail.userInteractionEnabled = NO;
    cell.accessoryType = UITableViewCellAccessoryNone;
    cell.userInteractionEnabled = YES;

    // Configure the cell...
    // based on the data type of the cell's contents

    // Set the cell's editable textField
    if ( [detailText isEqualToString:@"*"] ) {
        cellDetail.text = @"";
        cellDetail.placeholder = detailPlaceholder;
    } else {
        cellDetail.text = detailText;
    }
    cellDetail.keyboardType = UIKeyboardTypeASCIICapable;
    cellDetail.autocapitalizationType = UITextAutocapitalizationTypeWords;

    return cell;
}


#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    int rowNumber = indexPath.row;
    [tableView deselectRowAtIndexPath:indexPath animated:NO];
    NSLog(@"didSelectRow.. entered; row=%d, cellData=%@", rowNumber, self.TestData[rowNumber]);
    // The selected cell contains a textField with content to be edited
    // If another cell's textField is currently being edited, save its data before proceding
    UITableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath];
    UITextField * cellDetail = (UITextField *)[cell viewWithTag:99];
    NSLog(@"didSelectRow..: curently editing cell at [%p], selected cell at [%p]", self.fieldBeingEdited, cell);
    // if a textField is still in edit mode, show the Responder status of the cell and all in it
    if ( self.fieldBeingEdited != NULL ) {
        [self showCellResponderStatus:self.cellContainingField];
    }

    if ( self.fieldBeingEdited == NULL ) {
        NSLog(@"didSelectRow..: no field is being edited.");
    } else if ( cellDetail == self.fieldBeingEdited ) {
        // the cell selected is the same one being edited
        if ( ![self.fieldBeingEdited isFirstResponder] ) {
            // fieldBeingEdited is NOT the firstResponder. Try to make it the firstResponder.
            BOOL becameFirstResponder = [self.fieldBeingEdited becomeFirstResponder];
            NSLog(@"didSelectRow..: textField at [%p] returned %d from becomeFirstResponder.", self.fieldBeingEdited, becameFirstResponder);
            [self showCellResponderStatus:self.cellContainingField];
        }
    } else if ( cellDetail != self.fieldBeingEdited ) {
        // the cell selected is NOT the one being edited. Save the edited data and release the keyboard
        NSLog(@"didSelectRow..: field in cell with index=%d is being edited. Text=%@", self.cellIndex, self.fieldBeingEdited.text);
        BOOL resignedFirstResponder;
        [self showCellResponderStatus:self.cellContainingField];
        // This method call will log the responder status of this cell and all it is contained within
        //[self showViewHierarchy:self.fieldBeingEdited];
        resignedFirstResponder = [self.fieldBeingEdited resignFirstResponder];
        NSLog(@"didSelect..: resignFirstResponder on [%p] returned %d", self.fieldBeingEdited, resignedFirstResponder);
        //[self showViewHierarchy:self.fieldBeingEdited];
        if (resignedFirstResponder) {
            self.fieldBeingEdited = NULL;
            self.cellContainingField = NULL;
            self.cellIndex = -1; 
        }
    }

    // Enable the textField within the selected cell for in-place editing
    cellDetail.userInteractionEnabled = YES;
    cellDetail.enabled = YES;
    BOOL becameFirstResponder = [cellDetail becomeFirstResponder];
    NSLog(@"didSelectRow..: becomeFirstResponder returned %d", becameFirstResponder);
    if ( becameFirstResponder ) {
        // Update all the references and indexes for the cell and textField being edited.
        self.fieldBeingEdited = cellDetail;
        self.cellContainingField = cell;
        self.cellIndex = rowNumber;
    }

    NSLog(@"didSelectRow.. exit; textFieldBeingEdited.text=%@, cellIndex=%d", self.fieldBeingEdited.text, self.cellIndex);

}

-(BOOL) textFieldShouldBeginEditing:(UITextField *)textField
{
    NSLog(@"textFieldShouldBeginEditing entered");
    NSLog(@"... returning YES");
    return YES;
}

-(void) textFieldDidEndEditing:(UITextField *)textField
{
    NSLog(@"textFieldDidEndEditing entered with text: %@, cellIndex=%d", textField.text, self.cellIndex);
    NSInteger cellIndex = self.cellIndex;

    NSMutableArray * cellData = [NSMutableArray arrayWithArray:self.TestData[cellIndex]];
    [cellData replaceObjectAtIndex:1 withObject:textField.text];
    [self.TestData replaceObjectAtIndex:cellIndex withObject:cellData];
    textField.enabled = NO;

    [self.tableView reloadData];
    if ( [textField isFirstResponder] ) {
        NSLog(@"EditFlight(466): textFieldDidEndEditing; textField IS STILL the firstresponder!");
    }
    NSLog(@"textFieldDidEndEditing exit; cellData=%@", cellData);
}

-(BOOL) textFieldShouldReturn: (UITextField *)textField
{
    NSLog(@"textFieldShouldReturn; textField.text=%@", textField.text);
    [textField resignFirstResponder];
    BOOL resignedFirstResponder = [textField resignFirstResponder];
    NSLog(@"textFieldShouldReturn; resignFirstResponder returned %d",resignedFirstResponder);
    return YES;
}

-(void) showCellResponderStatus:(UITableViewCell*)cell
{
    NSLog(@"showCellResponderStatus entered");
    if ( [cell isFirstResponder] ) {
        NSLog(@"cell at [%p] IS first responder", cell);
    }else{
        NSLog(@"cell at [%p] IS NOT first responder", cell);
    }
    NSArray * cellSubViews = [[cell contentView] subviews];
    for ( UIView* uiv in cellSubViews ) {
        if ( [uiv isFirstResponder ]) {
            NSLog(@"subview at [%p] is a %@ and IS first responder", uiv, uiv.class);
        } else {
            NSLog(@"subview at [%p] is a %@ and IS NOT first responder", uiv, uiv.class);
        }
    }
}

-(void) showViewHierarchy:(UIView*) uiv
{
    NSLog(@"View Hierarchy");
    while (uiv != NULL) {
        BOOL isfirst = [uiv isFirstResponder];
        NSLog(@"view at [%p] is a %@, isFirstResponder=%d", uiv, uiv.class, isfirst);
        uiv = uiv.superview;
    }
    NSLog(@"view at [%p] is a %@", uiv, uiv.class);
    NSLog(@"End of view hierarchy");
}

@end
在将第二个文本字段设置为firstResponder之前,您可以询问第一个文本字段是否为firstResponder,然后进行更改:

但是first textField在辞去第一响应者的职务之前会询问其代表:

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
}

因此,如果您已经实现了此方法,请检查为什么返回NO。

您也可以使用开源作为键盘控件。它还包括其他键盘文本字段关系,可能会在过程中帮助您。 s
链接:

有一种更简单的方法。在didSelectRowAtIndexPath中,存储对当前单元格UITEXT字段的引用。然后您可以将其设置为第一响应者、处理编辑等

当您点击一个新单元格以选择它时,代理首先会收到一个DIDDENCEROWATINDEXPATH。在这里,您可以使用UITextField引用,可以退出第一响应程序,保存任何必要的状态,等等。然后在新单元格上调用DidDecleRowatingIndexPath,您就可以开始了。因此,类似于以下内容:

编辑:添加了附加代码


编辑格式以强调什么是代码我在第一个文本字段中调用resignFirstResponder,并在第二个文本字段中调用becomeFirstResponder之前确认它返回了1。文档中说新的textField必须在响应者链中,我假设是因为它是同一TableView的不同单元格中的一个textField.@a.Baker-您可以通过调用[textField becomeFirstResponder]告诉第二个textField成为响应者;你也可以问下一个赞助商。所有这些都是通过签出UIResponder类获得的,textfield继承自.@A.Baker-您可以通过重新指定所有textfields,然后将becomeFirstResponder发送到所选的一个来解决此问题。在回收UITableViewCells时,还有一件更重要的事情需要记住,即您没有在以前的文本字段上添加新的文本字段,从UITableViewCells用于的最后一个单元格。@A.Baker-如果以编程方式创建textField editAble属性,请确保该属性设置为YES。我相信我已经涵盖了所有基本内容,除了在所有textFields上辞职First Responder。在TableView的可重用单元格列表中保留所有文本字段的列表需要额外的工作。在我这么做之前,当我告诉当前的textField退出FirstResponder时,其他控件是否会自动成为FirstResponder?如果是这样,我如何找到它并让它辞职,这样我才能让我选择的文本字段成为第一反应者?显然,响应链对我来说仍然很神秘,所以请帮助我。谢谢你的建议和链接。我希望能够以任何顺序选择单元格,不仅仅是下一个和上一个,但如果我找不到方法使其工作,我会回到BSKeyboardControls。我相信这正是我正在做的。在我的问题代码中,有一个名为fieldBeingEdited的私有属性,这是您建议的参考。我的问题发生在试图使新选择的单元格的文本字段成为第一响应者时-它不会。是的,如果我的答案不清楚,很抱歉。我的主要观点是,我没有尝试在didSelectRowAtIndexPath中执行所有逻辑,而是在“didSelectRow…”和“didSelectRow…”中将其分为两个步骤。我的猜测是,您的函数中存在一些逻辑错误,与其尝试调试大型、复杂的方法,不如编写两个简单的方法。无论如何,我有一个工作项目,允许您切换单元格,如果您愿意,请告诉我,我将编辑我的答案以显示更多的代码。我希望看到一个工作示例。从你的回答中我感到困惑的是,你似乎有两个名称相同的方法——这怎么可能?也许你的代码会清除它。再次检查列表。方法有didSelect和did DE select。
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
}
// Code additions to Xcode's Master-Detail application template

// Use dynamic prototypes for table cells, custom cell class follows:

// MyCell.h
// Property is auto-synthesized, so there is nothing to see in MyCell.m

@interface MyCell : UITableViewCell
@property (nonatomic, weak) IBOutlet UITextField *tf;
@end



// MasterViewController.m

@interface MasterViewController () {
  NSMutableArray *_objects;
}
@property (nonatomic, weak) UITextField *selectedField;
@end


// this method is called when a cell is DE-selected
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
      [self.selectedField resignFirstResponder];
      // save state, etc if necessary
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyCell * cell = (MyCell *)[tableView cellForRowAtIndexPath:indexPath];
    self.selectedField = cell.tf; // cell.tf is a UITextField property defined on MyCell
    [self.selectedField becomeFirstResponder];
}