0

One goal of my app is to manage multiple custom input views (from nibs) along with the regular system keyboard. One or the other will always be present. (In the example code I am only managing a single nib + the system keyboard).

When swapping out the keyboards, I want to preserve the slide-in/slide-out effect you see with the system keyboard. To get the sliding animation for both the custom keyboard as well as the system keyboard I resignFirstResponder from the textfield and wait until the keyboard is hidden. Then I execute a becomeFirstResponder to bring in the new keyboard. I use the UIKeyboardDidHideNotification as a trigger to fire becomeFirstResponder and load the new keyboard.

The problem I see is that the UIKeyboardDidHideNotification is firing twice... once when executing resignFirstResponder (as expected) and again when becomeFirstResponder is executed. I suspect I can put in some state to detect the second notification, but I would like to understand, why becomeFirstResponder is causing it to fire in the first place?

#import "DemoViewController.h"

@interface DemoViewController ()
@property (strong, nonatomic) IBOutlet UITextField *dummyTextField;
@end

@implementation DemoViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[UITextField appearance] setKeyboardAppearance:UIKeyboardAppearanceDark];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];

    [self showNormalKeyboard];
    [_dummyTextField becomeFirstResponder];
}

-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

-(void)keyboardDidHide:(NSNotification*) notification {
    NSLog(@"Keyboard hidden: %@", notification);
    // Executing this next line causes the notification to fire again
    [_dummyTextField becomeFirstResponder];
}

-(void)showAlternateKeyboard{
    _dummyTextField.inputView = [[[NSBundle mainBundle] loadNibNamed:@"AlternateKeyboardView" owner:self options:nil] lastObject];
    [_dummyTextField resignFirstResponder];
}

-(void)showNormalKeyboard{
    _dummyTextField.inputView = nil;
    [_dummyTextField resignFirstResponder];
}

// This is a button on the DemoViewController screen
// that is always present.
- (IBAction)mainButtonPressed:(UIButton *)sender {
    NSLog(@"Main Screen Button Pressed!");
}

// This is a button from the Custom Input View xib
// but hardwired directly into here
- (IBAction)alternateKeyboardButtonPressed:(UIButton *)sender {
    NSLog(@"Alternate Keyboard Button Pressed!");
}

// This is a UISwitch on the DemoViewController storyboard
// that selects between the custom keyboard and the regular
// system keyboard
- (IBAction)keyboardSelectChanged:(UISwitch *)sender {
    if (sender.on){
        [self showAlternateKeyboard];
    } else {
        [self showNormalKeyboard];
    }
}

@end
Electro-Bunny
  • 1,380
  • 2
  • 12
  • 33

2 Answers2

2

My guess is that you are calling becomeFirstResponder before the previous field has fully finished resigning first responder. Just to see if it helps, try adding some delayed performance:

-(void)keyboardDidHide:(NSNotification*) notification {
    NSLog(@"Keyboard hidden: %@", notification);
    dispatch_async(dispatch_get_main_queue(), ^{
        [_dummyTextField becomeFirstResponder];
    };
}
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • that worked. I wonder if there is a way to synchronously be assured that resignFirstResponder is finished? – Electro-Bunny Mar 12 '14 at 21:19
  • That in effect is exactly what we are doing here. We are just cycling round one turn of the runloop, to let things settle down, before proceeding. There is nothing actually "asynchronous" about it. It is a minimal pause, resuming as soon as all code has finished executing. – matt Mar 12 '14 at 21:38
0
@interface MainViewController : UIViewController

@property (nonatomic, retain) IBOutlet UITextField *textField;
@property (nonatomic, retain) IBOutlet UISwitch *keyboardSwitch;
@property (nonatomic, retain) IBOutlet UISwitch *textFieldSwitch;


- (IBAction)toggleKeyboardVisibility;

@end


#import "MainViewController.h"

@implementation MainViewController
@synthesize textField, keyboardSwitch, textFieldSwitch;

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

/**
 * Show keyboard as soon as view appears
 *
 */
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.textField becomeFirstResponder];
}

/**
 * Modify the hidden property of our text field
 *
 */
- (IBAction)toggleTextFieldVisibility
{
    if (self.textFieldSwitch.on)
        self.textField.hidden = NO;
    else
        self.textField.hidden = YES;
}

/**
 * Change the first responder status of the keyboard
 *
 */
- (IBAction)toggleKeyboardVisibility
{
    if (self.keyboardSwitch.on)
        [self.textField becomeFirstResponder];
    else
        [self.textField resignFirstResponder];
}

@end
Durul Dalkanat
  • 7,266
  • 4
  • 35
  • 36