94

In several cases I want to add a toolbar to the top of the iPhone keyboard (as in iPhone Safari when you're navigating form elements, for example).

Currently I am specifying the toolbar's rectangle with constants but because other elements of the interface are in flux - toolbars and nav bars at the top of the screen - every time we make a minor interface change, the toolbar goes out of alignment.

Is there a way to programmatically determine the position of the keyboard in relation to the current view?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Rob Drimmie
  • 1,586
  • 1
  • 13
  • 16

7 Answers7

141

As of iOS 3.2 there's a new way to achieve this effect:

UITextFields and UITextViews have an inputAccessoryView property, which you can set to any view, that is automatically displayed above and animated with the keyboard.

Note that the view you use should neither be in the view hierarchy elsewhere, nor should you add it to some superview, this is done for you.

pablasso
  • 2,479
  • 2
  • 26
  • 32
tonklon
  • 6,777
  • 2
  • 30
  • 36
  • let me try . Though it looks the best way . – harshalb Aug 12 '10 at 06:42
  • Wow. What a find! Thanks (I did it the hard way and it's messy) – levous Dec 07 '11 at 15:46
  • 1
    I have a UIToolbar with a UITextField inside one of its bar button item but although I set the textFields inputAccessoryView to that toolbar in the first press the toolbar goes up but no keyboard appears. In the second press the keyboard appears with the toolbar have any idea about it ? – Ugur Kumru Feb 16 '12 at 14:45
  • But how to add a toolbar on UIWebView?:( – Dmitry Aug 09 '12 at 12:41
  • I have done it by replacing buttons on standard UIWebView toolbar (the same code as for removing it). – Dmitry Aug 09 '12 at 12:54
  • The view is removed from its superview when the keyboard shows so its safe to add it to something, in some cases its ever required, e.g.: when the view sets itself as the accessory [myView setInputAccessoryView:myView]; – valexa Sep 22 '12 at 15:43
72

So basically:

In the init method:

NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(keyboardWillShow:) name: UIKeyboardWillShowNotification object:nil];
[nc addObserver:self selector:@selector(keyboardWillHide:) name: UIKeyboardWillHideNotification object:nil];

And then have methods referred to above to adjust the position of the bar:

-(void) keyboardWillShow:(NSNotification *) note
{
    CGRect r  = bar.frame, t;
    [[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &t];
    r.origin.y -=  t.size.height;
    bar.frame = r;
}

Could make it pretty by animating the position change by wrapping it in

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];
//...
    [UIView commitAnimations];
  • Was poking around my old stuff this morning and noticed that this is a much better and the most comprehensive answer. Thanks! – Rob Drimmie Jul 12 '09 at 14:18
  • This answer is still quite relevant over a year later. It helped me get over the hump when developing something related to this. – james_womack Feb 05 '10 at 02:18
  • 2
    Just a warning to people stumbling across this question now: the UIKeyboardBoundsUserInfoKey is now deprecated in iPhone OS 3.2. There are other, similar ones such as `UIKeyboardFrameBeginUserInfoKey` that give the same information. – Stephen Darlington Jul 04 '10 at 17:36
  • 9
    There is even a better new way to do that in iOS3.2 the inputAccessoryView property on UITextField and UITextView. – tonklon Jul 23 '10 at 18:11
  • 6
    This answer helped a ton but is a bit dated. You should use `UIKeyboardFrameEndUserInfoKey` to get the final frame (in screen coordiantes) of the keyboard. You can also use `UIKeyboardAnimationDurationUserInfoKey` and `UIKeyboardAnimationCurveUserInfoKey` to get the rest of the parameters you need to exactly match the behavior of the keyboard. – Dave Peck Sep 03 '10 at 22:05
60

This is based on the existing answer from tonklon - I'm just adding a code snippet that shows a semi transparent black toolbar on top of the keyboard, together with a "done" button on the right:

UIToolbar *toolbar = [[[UIToolbar alloc] init] autorelease];
[toolbar setBarStyle:UIBarStyleBlackTranslucent];
[toolbar sizeToFit];
    
UIBarButtonItem *flexButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
UIBarButtonItem *doneButton =[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(resignKeyboard)];
    
NSArray *itemsArray = [NSArray arrayWithObjects:flexButton, doneButton, nil];

[flexButton release];
[doneButton release];
[toolbar setItems:itemsArray];

[aTextField setInputAccessoryView:toolbar];

and the -resignKeyboard looks like:

-(void)resignKeyboard {
  [aTextField resignFirstResponder];
}
starball
  • 20,030
  • 7
  • 43
  • 238
phi
  • 10,634
  • 6
  • 53
  • 88
  • 2
    Just adding a little comment on getting the next previous in place. UISegmentedControl *segmentControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Previous", @"Next", nil]]; [segmentControl setSegmentedControlStyle:UISegmentedControlStyleBar]; [segmentControl addTarget:self action:@selector(nextPrevious:) forControlEvents:UIControlEventValueChanged]; – Trausti Thor May 04 '12 at 09:32
  • 1
    An addition to @TraustiThor's comment: You have to wrap the segmented control in a UIBarButtonItem to add it to the toolbar. – Tim Büthe May 14 '12 at 19:35
  • Excellent - this is all the code I needed. Thanks for posting :) – Stretch Jul 04 '12 at 07:09
  • But what about UIWebView? How to add a toolbar to it? – Dmitry Aug 09 '12 at 12:41
24

If you register for keyboard notifications, ie UIKeyboardWillShowNotification UIKeyboardWillHideNotification, etc, the notification you receive will contain the bounds of the keyboard in the userInfo dict (UIKeyboardBoundsUserInfoKey).

See the UIWindow class reference.

raidfive
  • 6,603
  • 1
  • 35
  • 32
amrox
  • 6,207
  • 3
  • 36
  • 57
16

In 3.0 and above you can get the animation duration and curve from the userInfo dictionary of the notifications.

for instance, to animate the size of the view to make room for the keyboard, register for the UIKeyboardWillShowNotification and do something like the following:

- (void)keyboardWillShow:(NSNotification *)notification
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
    [UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];

    CGRect frame = self.view.frame;
    frame.size.height -= [[[notification userInfo] objectForKey:UIKeyboardBoundsUserInfoKey] CGRectValue].size.height;
    self.view.frame = frame;

    [UIView commitAnimations];
}

Do a similar animation for UIKeyboardWillHideNotification.

pablasso
  • 2,479
  • 2
  • 26
  • 32
David Beck
  • 10,099
  • 5
  • 51
  • 88
  • Looks like a better way to do it on the 3.0 SDK thanks for posting! – Hua-Ying Jan 28 '10 at 21:45
  • Thanks for the code. This helps a lot. But when I set my UITextView to become first responder in the viewDidLoad, the UIToolBar doesn't move with the resizing of the self.view. Do you have any idea why? – RyanJM Jul 01 '10 at 20:44
  • 1
    @RyanJM: becomeFirstResponder and resignFirstResponder have odd behavior when the view is off screen. You should be calling becomeFirstResponder from your viewWillAppear method instead. – David Beck Jul 03 '10 at 16:10
0

Create this method and call it on ViewWillLoad:

        - (void) keyboardToolbarSetup
{
    if(self.keyboardToolbar==nil)
        {
        self.keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];

        UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(anyAction)];

        UIBarButtonItem *extraSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];

        UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(anyOtherAction)];


        NSArray *toolbarButtons = [[NSArray alloc]initWithObjects:cancelButton,extraSpace,doneButton, nil];

        [self.keyboardToolbar setItems:toolbarButtons];

        self.myTextView.inputAccessoryView=self.keyboardToolbar;
        }
}
-3

There's no way (AFAIK) to get the dimensions of the keyboard view. It is however constant, at least in every iPhone version so far.

If you calculate the toolbar position as an offset from the BOTTOM of your view, and take the size of your view into account, then you should not have to worry whether a navbar is present or not.

E.g.

#define KEYBOARD_HEIGHT 240 // example - can't remember the exact size
#define TOOLBAR_HEIGHT 30

toolBarRect.origin.y = viewRect.size.height - KEYBOARD_HEIGHT - TOOLBAR_HEIGHT;

// move toolbar either directly or with an animation

Instead of a define, you could easily create a keyboardHeight function that returns the size based on whether the keyboard is being displayed, and move this toolbar positioning into a separate function that reorganizes your layout.

Also it can depend on where you do this positioning as it's possible the size of your view may change between being loaded and shown based on your navbar setup. I believe the best place to do it would be in viewWillAppear.

Rui Peres
  • 25,741
  • 9
  • 87
  • 137
Andrew Grant
  • 58,260
  • 22
  • 130
  • 143
  • This worked great, thanks! So far I've been doing this calculation in the selector triggered by UIKeyboardDidShowNotification. I've only tested in a couple of places, but it looks like a good spot. – Rob Drimmie Oct 01 '08 at 18:00
  • As of 5.0 the keyboard size is no longer static. – Alastair Stuart Oct 24 '11 at 16:37