How can I enable/disable a UITextField's inputAccessoryView buttons?

2.8k views Asked by At

I've created a UIToolBar as an inputAccessoryView with a next and previous buttons that will cycle through the textFields in my view controller.

I've created a category on UITextField based on the third SO answer on this page which adds a property to the textField that points to the next/previous textField.

I can get it to cycle through the textFields both forward and backward, but only once, and then my buttons are permanently disabled. Also, when the last textField is in focus, I still need to tap the next button one extra time (4 taps for 3 textFields) to have it disable the next button — same with the previous button, I have to tap back once when I'm in the first textField.

// ViewController.h
@interface DetailViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate, UIPopoverControllerDelegate> {

    __weak IBOutlet UITextField *valueField;
    __weak IBOutlet UITextField *nameField;
    __weak IBOutlet UITextField *serialNumberField;

}

@property (nonatomic, strong) UITextField *currentTextField;

- (IBAction)nextTextField:(id)sender;
- (IBAction)prevTextField:(id)sender;


// ViewController.m
- (void)viewDidLoad {
    //...

    nameField.delegate = self;
    nameField.nextTextField = serialNumberField;
    nameField.prevTextField = nil;

    serialNumberField.delegate = self;
    serialNumberField.nextTextField = valueField;
    serialNumberField.prevTextField = nameField;

    valueField.delegate = self;
    valueField.prevTextField = serialNumberField;
    valueField.nextTextField = nil;

    //...
}

- (void)viewWillAppear:(BOOL)animated {
    //UIToolBar for inputAccessoryView
    UIToolbar *toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)];
    UIBarButtonItem *nextField = [[UIBarButtonItem alloc] initWithTitle:@"\U00003009"
                                                                  style:UIBarButtonItemStylePlain
                                                                 target:self
                                                                 action:@selector(nextTextField:)];
    UIBarButtonItem *prevField = [[UIBarButtonItem alloc] initWithTitle:@"\U00003008"
                                                                  style:UIBarButtonItemStylePlain
                                                                 target:self
                                                                 action:@selector(prevTextField:)];
    UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
    UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(backgroundTapped:)];
    NSArray *toolBarButtons = @[prevField, nextField, space, done];
    toolBar.items = toolBarButtons;

    nameField.inputAccessoryView  = toolBar;
    valueField.inputAccessoryView = toolBar;
    serialNumberField.inputAccessoryView = toolBar;
}

    - (IBAction)nextTextField:(id)sender {
    UITextField *next = self.currentTextField.nextTextField;

    if (!next) {
        [sender setEnabled:NO];
    } else {
        [sender setEnabled:YES];
        [next becomeFirstResponder];
    }
}


- (IBAction)prevTextField:(id)sender {
    UITextField *prev = self.currentTextField.prevTextField;

    if (!prev) {
        [sender setEnabled:NO];
    } else {
        [sender setEnabled:YES];
        [prev becomeFirstResponder];
    }
}
1

There are 1 answers

0
tjboneman On BEST ANSWER

I think part of the problem is that you are handling the enabling/disabling of the bar buttons in a method that is only called once one of the bar buttons has been tapped. It would be better to set the barbuttonitems as properties of your view controller (so you can enable/disable them when you want to), and then handle the enabling/disabling of the bar button items within the UITextField's 'textFieldShouldBeginEditing' delegate method.

So, something like this:

- (void)viewWillAppear:(BOOL)animated {
    //UIToolBar for inputAccessoryView
    UIToolbar *toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)];
    self.moveToNextFieldButton = [[UIBarButtonItem alloc] initWithTitle:@"\U00003009"
                                                                  style:UIBarButtonItemStylePlain
                                                                 target:self
                                                                 action:@selector(nextTextField:)];
    self.moveToPrevFieldButton = [[UIBarButtonItem alloc] initWithTitle:@"\U00003008"
                                                                  style:UIBarButtonItemStylePlain
                                                                 target:self
                                                                 action:@selector(prevTextField:)];
    UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
    UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(backgroundTapped:)];
    NSArray *toolBarButtons = @[self.moveToPrevFieldButton, self.moveToNextFieldButton, space, done];
    toolBar.items = toolBarButtons;

    nameField.inputAccessoryView  = toolBar;
    valueField.inputAccessoryView = toolBar;
    serialNumberField.inputAccessoryView = toolBar;
}

-(BOOL)textFieldShouldBeginEditing:(UITextField*)textField{

    UITextField *next = textField.nextTextField;
    UITextField *prev = textField.prevTextField;
    self.moveToNextFieldButton.enabled = next != nil;
    self.moveToPrevFieldButton.enabled = prev != nil;

    return YES;
}

- (IBAction)nextTextField:(id)sender {
    UITextField *next = self.currentTextField.nextTextField;

    if (next) {
        [next becomeFirstResponder];
    } 
}


- (IBAction)prevTextField:(id)sender {
    UITextField *prev = self.currentTextField.prevTextField;

    if (prev) {
        [prev becomeFirstResponder];
    }
}