Programmatically align a toolbar on top of the iPhone keyboard
IosIphoneCocoa TouchKeyboardIos Problem Overview
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?
Ios Solutions
Solution 1 - Ios
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.
Solution 2 - Ios
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];
Solution 3 - Ios
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];
}
Hope that helps someone.
Solution 4 - Ios
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.
Solution 5 - Ios
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
.
Solution 6 - Ios
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;
}
}
Solution 7 - Ios
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.