Getting the frame of the keyboard at the window level - iphone

I am trying to get the coordinates of the frame of the keyboard at window level to be compared with a TableCell to make minor adjustments in scrolling so a selected TextField in the cell wont be behind the keyboard or the header.
However I am having issue getting the x,y coordinates for the keyboard at window level, they simply return 0,0 (origin) which is not true. Or if it is, is not what I need.
I am using the lines:
CGRect keyboardFrame = [self.view.window convertRect:_numericKeyboardView.frame toView:nil];
NSLog(#"Keyboard Frame: %f, %f, %f, %f", keyboardFrame.origin.x, keyboardFrame.origin.y, keyboardFrame.size.width, keyboardFrame.size.height);
which produces the output(for portrait):
Keyboard Frame: 0.000000, 0.000000, 768.000000, 264.000000
when it should be more like:
Keyboard Frame: 415.000000, 0.000000, 768.000000, 264.000000
Any ideas on how to get the correct keyboard coordinates?

From http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
You need to register for keyboard notification like
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
then
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGRect kbFrame = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
}
And do not forget to remove self form notification center in your dealloc method

If you want to do it this way, you may be sending convertRect:toView: to the wrong receiver. Do [_numericKeyboardView convertRect:_numericKeyboardView.bounds toView:nil];

Your question is misleading. It sounds like your "keyboard" is a custom view , not the system keyboard. The question should be "getting the frame of a view in window coordinates". The convertRect:toView method will work but you have it backwards.
Change your code to:
CGRect keyboardFrame = [_numericKeyboardView convertRect:_numericKeyboardView.bounds
toView:self.view.window];

Related

Why does resignFirstResponder reset contentOffset on a UITextView?

No matter which settings I try to change, resignFirstResponder always forces the text to scroll to the top of a UITextView
I would like the keyboard to disappear but the content offset to remain the same.
Any solutions would be greatly appreciated.
This is caused by a UIScrollView bug in iOS 8. Hopefully in iOS 9 they fix it. I only assume that its a bug because resignFirstResponder didn't have this behavior in iOS 7. Maybe its a feature.
The only way I found to prevent resignFirstResponder and setText from resetting the contentOffset was the following:
Swift:
textView.layoutManager.allowsNonContiguousLayout = false
Objective C
textView.layoutManager.allowsNonContiguousLayout = NO;
Another reason I think its a bug is because the documentation says that it is set to NO by default, but when you print out the variable without setting it, its true.
More info on NSLayoutManager can be found here
in viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
add this function
- (void)keyboardWillHide:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:animationDuration animations:^{
[_textView scrollRangeToVisible:NSMakeRange([_textView.text length], 0)];
}];
}

Adjust static UITableView when keyboard appears?

I am working on a app which contains UITextField and UITextView in static UITableView. I am facing two issues
When I select UITextField it moved correctly but it is not working for UITextView.
When the keyboard disappears the UITableView not shown properly.
All I want is to adjust UITextField and UITextView accordingly when keyboard appears and disappears. Here is my code.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
- (void)keyboardWillShow:(NSNotification *)notification {
//get the end position keyboard frame
NSDictionary *keyInfo = [notification userInfo];
CGRect keyboardFrame = [[keyInfo objectForKey:#"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
//convert it to the same view coords as the tableView it might be occluding
keyboardFrame = [self.tableView convertRect:keyboardFrame fromView:nil];
//calculate if the rects intersect
CGRect intersect = CGRectIntersection(keyboardFrame, self.tableView.bounds);
if (!CGRectIsNull(intersect)) {
//yes they do - adjust the insets on tableview to handle it
//first get the duration of the keyboard appearance animation
NSTimeInterval duration = [[keyInfo objectForKey:#"UIKeyboardAnimationDurationUserInfoKey"] doubleValue];
//change the table insets to match - animated to the same duration of the keyboard appearance
[UIView animateWithDuration:duration animations:^{
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, intersect.size.height, 0);
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, intersect.size.height, 0);
}];
}
}
- (void) keyboardWillHide: (NSNotification *) notification{
NSDictionary *keyInfo = [notification userInfo];
NSTimeInterval duration = [[keyInfo objectForKey:#"UIKeyboardAnimationDurationUserInfoKey"] doubleValue];
//clear the table insets - animated to the same duration of the keyboard disappearance
[UIView animateWithDuration:duration animations:^{
self.tableView.contentInset = UIEdgeInsetsZero;
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}];
}
Try with the UITableView's instance method: scrollToRowAtIndexPath:atScrollPosition:animated:
In keyBoardWillShow use the tableview's or the textfield's index path and set the scroll position to UITableViewScrollPositionTop.
If it doesn't work as you want it to, try with scrollToRowAtIndexPath:atScrollPosition:animated:
Hope this helps, good luck! :))
For further information, check out Apple's UITableView reference: https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html
You can use a third party library for avoiding keyboard scrolling above of text field or text views.
Use https://github.com/cokecoffe/ios-demo/blob/master/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m
OR
You can use UITableviewController as your view controller class.Change the Tableview content property to Static. Then you can add any UI controls to the static cells of the table view from story board.
If you select any text field or text view in your tableview, then key board automatically shows below of your text field or text view without any programming logic.

Scrolling view when keyboard appears: Library or DIY?

I've had it up to my neck trying to make the whole view moving to the appropriate UITextField when a user taps on a respective object look seamless. I know that I'm not the only one that absolutely hates doing this too.
What is the best approach to making this work as beautifully as possible with the least amount of work possible?
I've tried out TPKeyboardAvoiding, and it totally sucked.
Right now, I've got this code going, but it sucks as well in it's own special way:
- (void)viewDidLoad
{
self.view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin);
self.scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWasShown:(NSNotification *)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, self.activeField.frame.origin.y-kbSize.height);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification *)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
For me, it works TPKeyboardAvoiding , I use it in all my projects. Have you tried to:
Add a UIScrollView into your view controller's xib
Set the scroll view's class to TPKeyboardAvoidingScrollView (still
in the xib, via the identity inspector)
Place all your controls within that scrollview ?
I also find this solution :Keyboard Manager
Download demo project
Just drag and drop KeyboardManager and SegmenedNextPrevious classes to your project
In your appDelegate write only one line of code:
[KeyBoardManager installKeyboardManager];
Good luck!
Did you know that if your view controller derives from UITableViewController, scrolling the table view is automatic when the keyboard displays when a text field or text view gets focus? You don't have to do anything. That being said, it may not work well for your app to refactor the UI so that it uses a table view with static cells instead of whatever you're doing now, though.
If this wont work for you, you can change your scroll view's contenSize to be the size of your visible area when the keyboard is shown and then call scrollRectToVisible:animated: on your scroll view passing it the rect of your textfield inside of your keyboardWasShown: selector.

Keyboard "WillShow" and "WillHide" vs. Rotation

I've got a view controller listening for both UIKeyboardWillShowNotification and UIKeyboardWillHideNotification. The handlers for these notifications adjust various parts of the view, which is standard procedure.
The following code is used to convert the keyboard rect from screen coordinates:
CGRect keyboardBounds = [self.view convertRect:[keyboardBoundsValue CGRectValue] fromView:nil];
Again, standard procedure. Unfortunately, there is a critical situation where this conversion fails. Look at what happens when an iPhone is rotated from portrait to landscape while the keyboard is deployed:
1) iOS automatically fires UIKeyboardWillHideNotification; self.interfaceOrientation is reported as portrait; keyboardBounds.height is 216.0. This makes sense. Why? Because the notification handler is given the chance to "clean up" before the view switches to landscape mode.
2) iOS automatically fires UIKeyboardWillShowNotification; self.interfaceOrientation is reported as portrait; keyboardBounds.height is 480.0. This does NOT make sense. Why not? Because the notification handler is going to do its work thinking that the height of the keyboard is 480.0!
Did Apple drop the ball on this one, or am I doing something wrong?
Please note that listening instead for UIKeyboardDidShowNotification is not a valid solution, because it significantly degrades the user experience. Why? Because animating my changes to the view after the keyboard deployment animation occurs is... well, pretty terrible-looking.
Has anyone managed to get autorotation working perfectly while the keyboard is deployed? It seems like an explosion of chaos that Apple has completely overlooked. >:|
Maybe a bit late, but I've just run into the same issue and have a nice solution for it that avoids any kind of work arounds (unless of course apple change things)
Basically, when the notification center calls your method for UIKeyboardWillShowNotification (or any of the other notifications), the frame that it gives you for UIKeyboardFrameBeginUserInfoKey is in context of the window, NOT your view. The problem with this, is that the windows coordinate system is always in portrait, regardless of the devices orientation, hence you're finding the width and height the wrong way round.
If you want to avoid your work around, simply convert the rectangle into the coordinate system of your view (which does change according to the orientation). To do this, do something like the following :
- (void) keyboardWillShow:(NSNotification *)aNotification
{
CGRect keyboardFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect convertedFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
......
/* Do whatever you want now with the new frame.
* The width and height will actually be correct now
*/
......
}
Hopefully this should be what you're after :)
Recently I've wrote a blog post about this exact problem you've described and how to solve it with a short and elegant way. Here is the link to the post: Synchronizing rotation animation between the keyboard and the attached view
If you don't want to dive into the long explanation described in the blog post here is a short description with a code example:
The basic principle is to use the same method that everyone uses - observing keyboard notifications to animate the attached view up and down. But in addition to that, you have to cancel these animations when the keyboard notifications are fired as a consequence of interface orientation change.
Rotation example without animation cancellation custom on interface orientation change:
Rotation example with animation cancellation on interface orientation change:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(adjustViewForKeyboardNotification:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(adjustViewForKeyboardNotification:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter]
removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]
removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
self.animatingRotation = YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
self.animatingRotation = NO;
}
- (void)adjustViewForKeyboardNotification:(NSNotification *)notification {
NSDictionary *notificationInfo = [notification userInfo];
// Get the end frame of the keyboard in screen coordinates.
CGRect finalKeyboardFrame = [[notificationInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// Convert the finalKeyboardFrame to view coordinates to take into account any rotation
// factors applied to the window’s contents as a result of interface orientation changes.
finalKeyboardFrame = [self.view convertRect:finalKeyboardFrame fromView:self.view.window];
// Calculate new position of the commentBar
CGRect commentBarFrame = self.commentBar.frame;
commentBarFrame.origin.y = finalKeyboardFrame.origin.y - commentBarFrame.size.height;
// Update tableView height.
CGRect tableViewFrame = self.tableView.frame;
tableViewFrame.size.height = commentBarFrame.origin.y;
if (!self.animatingRotation) {
// Get the animation curve and duration
UIViewAnimationCurve animationCurve = (UIViewAnimationCurve) [[notificationInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
NSTimeInterval animationDuration = [[notificationInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// Animate view size synchronously with the appearance of the keyboard.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
[UIView setAnimationBeginsFromCurrentState:YES];
self.commentBar.frame = commentBarFrame;
self.tableView.frame = tableViewFrame;
[UIView commitAnimations];
} else {
self.commentBar.frame = commentBarFrame;
self.tableView.frame = tableViewFrame;
}
}
This answer was also posted in similar question: UIView atop the Keyboard similar to iMessage App
I met the same problem. iOS gaves me incorrect width/height of the keyboard. I used the following snipped in a keyboardDidShow handler:
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGSize keyboardSize2 = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
LogDbg(#"keyboard size: frameBegin=%#; frameEnd=%#", NSStringFromCGSize(keyboardSize), NSStringFromCGSize(keyboardSize2));
and for portrait and landscape modes of iPad I got respectively:
2012-06-14 04:09:49.734 -[LoginViewController keyboardDidShow:] 132 [DBG]:keyboard size: frameBegin={768, 264}; frameEnd={768, 264}
2012-06-14 04:10:07.971 -[LoginViewController keyboardDidShow:] 132 [DBG]:keyboard size: frameBegin={352, 1024}; frameEnd={352, 1024}
Guessing that the width of the keyboard should be greater then the height (yep, i'm so naive) I made a workaround like following:
if (keyboardSize.width < keyboardSize.height)
{
// NOTE: fixing iOS bug: http://stackoverflow.com/questions/9746417/keyboard-willshow-and-willhide-vs-rotation
CGFloat height = keyboardSize.height;
keyboardSize.height = keyboardSize.width;
keyboardSize.width = height;
}
Well, try looking at keyboard width. If it is the value that you are expecting, then I assume that the values are simply switched ;). 480 makes sense as a keyboard width for going into landscape, which is what gives me this hunch.
If that fails, just store the portrait and landscape rectangles separately. They are well documented ;)
I know this a very very late reply. Now only I came on this situation and find the unanswered question. So I thought I'll share my solution. There will be some other better way, but the following way also we can solve this.
The KBKeyboardHandler that I used is from: UITextField: move view when keyboard appears
I just changed my delegate as following:
- (void)keyboardSizeChanged:(CGSize)delta
{
CGRect frame = self.view.frame;
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
switch (interfaceOrientation) {
case UIInterfaceOrientationPortrait:
frame.origin.y-=delta.height;
break;
case UIInterfaceOrientationPortraitUpsideDown:
frame.origin.y+=delta.height;
break;
case UIInterfaceOrientationLandscapeLeft:
frame.origin.x-=delta.height;
break;
case UIInterfaceOrientationLandscapeRight:
frame.origin.x+=delta.height;
break;
default:
break;
}
self.view.frame = frame;
}
And it was working fine.
Here is my workaround:
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
float keyboardHeight = self.interfaceOrientation == UIInterfaceOrientationPortrait ? keyboardSize.height : keyboardSize.width;
Hope this helps :)
I use the following code to get the size of the keyboard which works fine for all rotations
NSDictionary *info = [aNotification userInfo];
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
kbHeight = [[NSNumber numberWithFloat:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.width] floatValue];
else
kbHeight = [[NSNumber numberWithFloat:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height] floatValue];
NSLog(#"keyboard height = %F",kbHeight);
I then test for the orientation using the status bar orientation (which works in the first launch case for the iPad) and shift the view in the relative direction needed to make space for the keyboard. This works perfectly, if the keyboard is visible then it relocates to the correct position on rotations.
UIDeviceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (orientation == UIDeviceOrientationPortrait)
{
NSLog(#"Orientation: portrait");
self.originalCenter = self.view.center;
self.view.center = CGPointMake(self.originalCenter.x, self.originalCenter.y-kbHeight);
}
if (orientation == UIDeviceOrientationPortraitUpsideDown)
{
NSLog(#"Orientation: portrait upside down");
self.originalCenter = self.view.center;
self.view.center = CGPointMake(self.originalCenter.x, self.originalCenter.y+kbHeight);
}
if (orientation == UIDeviceOrientationLandscapeLeft)
{
NSLog(#"Orientation: landscape left");
self.originalCenter = self.view.center;
self.view.center = CGPointMake(self.originalCenter.x+kbHeight,self.originalCenter.y);
}
if (orientation == UIDeviceOrientationLandscapeRight)
{
NSLog(#"Orientation: landscape right");
self.originalCenter = self.view.center;
self.view.center = CGPointMake(self.originalCenter.x-kbHeight,self.originalCenter.y);
}
You can return the view to its original position when the keyboard disappears or via a textFileDidEndEditing function.

iPhone - How may I scroll a UITextField and resize a UITextView when keybord appears

I have a UIScrollView inside which I have a UITextField and a UITextView.
I want to prevent the keyboard to hide the field we are working on.
If my user tap on the TextField, I want it to scroll to top of the screen.
If my user tap on the TextView, I want it to resize to the empty place left on the top of the keyboard.
My keyboard has an accessory view (a toolbar).
For now, I catch the UIKeyboardWillShowNotification notification, and I use this code, that does not really work (the textview is resized, but is still behind the keyboard) :
This code worked well when I hadn't the UIScrollView.
- (void)keyboardWillShow:(NSNotification *)notification {
if ([self.noteView isFirstResponder] == NO) return;
/*
Reduce the size of the text view so that it's not obscured by the keyboard.
Animate the resize so that it's in sync with the appearance of the keyboard.
*/
NSDictionary *userInfo = [notification userInfo];
NSValue* aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]; // Get the origin of the keyboard when it's displayed.
NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]; // Get the duration of the animation.
// Get the top of the keyboard as the y coordinate of its origin in self's view's coordinate system. The bottom of the text view's frame should align with the top of the keyboard's final position.
CGRect keyboardRect = [aValue CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
CGFloat keyboardTop = keyboardRect.origin.y;
CGRect newTextViewFrame = self.view.bounds;
newTextViewFrame.size.height = keyboardTop - self.view.frame.origin.y; // Using bounds here does not help
NSTimeInterval animationDuration;
[animationDurationValue getValue:&animationDuration];
// Animate the resize of the text view's frame in sync with the keyboard's appearance.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:animationDuration];
self.noteView.frame = newTextViewFrame;
[UIView commitAnimations];
}
So could you help me to :
make this work for the textview ?
make the Textview go back to its original position ?
How may I do to scroll the TextField without any resizing feature ?
Thank you
Could you just programmatically scroll the uiscrollview using this:
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated
Not sure if this will work for your application or not.