Show keyboard with UITextField in the input accessory view

ios, uitextfield

Solution

You cannot summon the keyboard without an object which can become first responder. There are two ways to work around:

Subclass a `UIView` and implement `UIKeyInput` protocol in it. For example:

In your .h file:

@interface InputObject : UIView<UIKeyInput>
@property (nonatomic, copy) NSString *text;
@property (nonatomic, strong) UIView *inputAccessoryView; // You must override inputAccessoryView , since it's readonly by default
@end

In your .m file, implement the protocol:

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (BOOL)hasText
{
    return [self.text length];
}

- (void)insertText:(NSString *)text
{
    NSString *selfText = self.text ? self.text : @"";
    self.text = [selfText stringByAppendingString:text];
    [[NSNotificationCenter defaultCenter] postNotificationName:kInputObjectTextDidChangeNotification object:self];
}

- (void)deleteBackward
{
    if ([self.text length] > 0)
        self.text = [self.text substringToIndex:([self.text length] - 1)];
    else
        self.text = nil;
    [[NSNotificationCenter defaultCenter] postNotificationName:kInputObjectTextDidChangeNotification object:self];
}

Let's say you want to summon the keyboard in your `-viewDidAppear:`, the code goes like this:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // inputObject and textField are both your ivars in your view controller
        inputObject = [[InputObject alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
        inputObject.inputAccessoryView = textField;
        [self.view addSubview:inputObject];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputObjectTextDidChange:) name:kInputObjectTextDidChangeNotification object:nil];

    }

    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        [inputObject becomeFirstResponder]; // This will summon the keyboard
    }

Then implement the notification selector in your view controller:

    - (void)inputObjectTextDidChange:(NSNotification *)notification
    {
        // Just set the text here. notification.object is actually your inputObject.
        textField.text = ((InputObject *)(notification.object)).text;
    }

This is probably what you mean by "set the inputAccessoryView without having a UITextField initially"

- Another workaround is to let the textField "pretend" to be the `inputAccessoryView` by carefully arranging its animation. But this solution needs your textField to be the first responder.

Firstly, you observe the keyboard events in your `-viewDidLoad`:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Init the textField and add it as a subview of self.view
        textField = [[UITextField alloc] init];
        textField.backgroundColor = [UIColor redColor];
        [self.view addSubview:textField];

        // Register keyboard events
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowNotification:) name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHideNotification:) name:UIKeyboardWillHideNotification object:nil];
    }

Secondly, set the frame of your `textField` in `-viewWillAppear:`, in order to guarantee its frame won't be affected by autoresizing :

    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        textField.frame = CGRectMake(0, CGRectGetMaxY(self.view.bounds), CGRectGetWidth(self.view.bounds), 50);
    }

And then, arrange your `textField` animation and let it be synchronized with the keyboard animation. Your keyboard notification selectors may be like this:

    - (void)keyboardWillShowNotification:(NSNotification *)notification
    {
        NSDictionary *userInfo = notification.userInfo;
        UIViewAnimationCurve curve = [[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue];
        CGFloat duration = [[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
        CGRect keyboardFrame = [[userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
        keyboardFrame = [self.view convertRect:keyboardFrame toView:self.view];

        [UIView animateWithDuration:duration delay:0.0f options:(UIViewAnimationOptions)curve animations:^{
            CGRect textFieldFrame = textField.frame;
            textFieldFrame.origin.y = keyboardFrame.origin.y - CGRectGetHeight(textFieldFrame);
            textField.frame = textFieldFrame;
        }completion:nil];
    }

    - (void)keyboardWillHideNotification:(NSNotification *)notification
    {
        NSDictionary *userInfo = notification.userInfo;
        UIViewAnimationCurve curve = [[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue];
        CGFloat duration = [[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
        [UIView animateWithDuration:duration delay:0.0f options:(UIViewAnimationOptions)curve animations:^{
            textField.frame = CGRectMake(0, CGRectGetMaxY(self.view.bounds), CGRectGetWidth(self.view.bounds), 50);
        }completion:nil];
    }

At last, call `[textField becomeFirstResponder]` to trigger the animation.

Problem

I have a buttonA and when the buttonA is clicked, I want the keyboard to show with a UITextField in the inputAccessoryView. Is there a way to have the keyboard show manually and also set the inputAccessoryView without having a UITextField initially? Thanks.

Original source

Related problems