6

I'm trying to achieve a similar keyboard interaction that Messages has in iOS 7. I have a UIView which contains a UITextView, and when the user selects it to start typing, I want to make this UIView the inputAccessoryView. This would take care of the animation for me, as well as the new UIScrollView keyboard dismiss interaction in iOS 7.

When the UITextView begins editing, I'm trying to set its inputAccessoryView to its parent UIView (which is already in the view hierarchy). The keyboard appears but not with an accessory view.

I've read some people are using a duo of UITextFields to make this work, but that seems like a bad way to achieve this.

Any suggestions?

Peter Foti
  • 5,526
  • 6
  • 34
  • 47
Ricky
  • 3,101
  • 1
  • 25
  • 33

3 Answers3

12

A much easier solution is to make your input field the input accessory view of your view controller:

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (UIView *)inputAccessoryView
{
    return self.yourInputField;
}

The view will be on screen at the bottom of the screen and when it becomes first responder in response to a user tapping it, the keyboard will be presented. The view will be animated such that it remains immediately above the keyboard.

pr1001
  • 21,727
  • 17
  • 79
  • 125
  • Wow, I wish I could upvote this ~20 times. I've been looking for the best way to do this for like an hr. (Some build the TextView and its other views/buttons *all* in code, some use XIBs, some use inputAccessory, some use keyboard notifications) but finally found this - definitely the best way! I dragged the UIView (containing the TextView) I want to use as the `inputAccessory` to the main storyboard area (_not_ within the ViewController's view hierarchy), hooked it up as an IBOutlet & used the above code. It works exactly as expected, thanks! Plus you can use interactive keyboard dismissal. – taber Feb 28 '19 at 00:33
  • Is there any solution for iPhone X and above device having notch at bottom? In this case, needed to add safe area insets at bottom side. How can we do this? – Mehul Thakkar Oct 18 '19 at 12:21
0

The only way to get this to work is via a second text field. The idea is to make it a subview but not visible (due to crazy rect). You then switch firstResponder back and forth between it and the real text field while its getting delegate methods. I created a some one viewController test project and did this (you can copy paste and verify behavior with about 2 minutes of time):

@implementation ViewController
{
    UITextField *field;
    UITextField *dummyView;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    field = [[UITextField alloc] initWithFrame:CGRectMake(0, 460, 320, 20)];
    field.borderStyle = UITextBorderStyleRoundedRect;
    field.delegate = self;
    //field.inputAccessoryView = field;
    field.text = @"FOO";
    [self.view addSubview:field];

    dummyView = [[UITextField alloc] initWithFrame:CGRectMake(0, 40000, 320, 20)];
    dummyView.delegate = self;
    [self.view addSubview:dummyView];
}

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
    if(textField == field && textField.superview == self.view) {
        [field removeFromSuperview];
        dummyView.inputAccessoryView = field;

        [dummyView becomeFirstResponder];
    }
    return YES;
}

@end

I should add I've used this technique in shipping apps since iOS 4.

EDIT: So a couple of other ideas:

1) To make the glitch when the keyboard starts moving look a little better, you could take a snapshot of your textView, put that into a UIImageView, and when you remove the textView from the primary view, replace it with the UIImageView. Now the appearance is the same. Add an animation for the image so that noting happens for 50 ms, then the alpha goes to 0. Add a similar animation to your real textview, so that it has an alpha of 0 for 50 ms, then it goes to 1. You may be able to tweak this so the transition is good (but not great).

2) The way apple probably does this is to get the animation curve and timing from the keyboard moving notification. In this case they would add a accessory view with 0 height at first, and animate the textField so its tracking the keyboard, but above it. Both moving same distance at the same time. At the end of the animation, the textField is pulled out of self.view, the accessory view has its frame changed to have the height of the textField, and the textField is placed as a subview of the accessory container view. This should work but yeah, its a bit complex to do. If you want someone to code it for you offer a 100 pt bounty. You still need the dummy text field for when you go and move the textField at the end, since when you take it out of its containing view it will resign first responder. So at the end, you make the dummy field the first responder, move the textfield, then make the real textfield the first responder again.

David H
  • 40,852
  • 12
  • 92
  • 138
  • 1
    Thanks David. I'll try this out and update when I can. A shame that Apple don't provide a better way to do this. – Ricky Nov 21 '13 at 02:44
  • My personal opinion is, it's not so much code, it uses principals that were often used on OSX, and frankly I've other bugs and features d like them to work on. I have a complex collection view that I cannot incrementally update (it crashes) so on any change must reload it all! – David H Nov 21 '13 at 12:34
  • I managed to get this mostly working with my UITextViews, however I notice that the UIKeyboard animates up with the inputAccessoryView from the bottom of the screen. So for a very short time, you can see one view coming over the other. Have you noticed this before? – Ricky Nov 26 '13 at 21:46
  • I just threw this together, and in my test it looked pretty good in the simulator. Not sure how you could see two views - my code takes the primary one and pulls it out of the self.view, and attaches it as the accessory view. What's missing there is the switch back to the textView once it does become the first responder. – David H Nov 26 '13 at 23:14
0

This actually works best if you don't use .inputAccessoryView at all and instead just animate the position of the parent UIView as the keyboard opens and closes. Here is an answer describing the process step-by-step with all the code.

Community
  • 1
  • 1
Nestor
  • 2,753
  • 2
  • 29
  • 34
  • The problem with that approach is that it doesn't work with interactive keyboard dismissal. It only works for regular keyboard appearance/disappearance. – greenisus Mar 28 '14 at 22:47