I have a custom inherited UIView class with a UITableView within it as its only subview. I'm trying to mimic the normal functionality of the UITableViewController when the keyboard is shown to adjust the contentInset and scrollIndicatorInsets of the table view to the height of the keyboard. This is my method that gets called when the keyboard did show from within my custom UIView class:
- (void)keyboardDidShow:(NSNotification*)notification
{
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
_tableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
_tableView.scrollIndicatorInsets = _tableView.contentInset;
}
This works to a certain extent, but there is still some overlap of the keyboard onto the table view for some reason by maybe ten or so pixels.
I'm thinking it has something to do with not taking into account some of the other screen geometry but I don't see how that could be. The height of the keyboard should be exactly what I need because the tableView stretches all the way to the bottom of the screen. Any ideas?
Change the tableView.frame.size.height, to account for the keyboard.
when keyboard is showing, reduce the height,
when not showing, increase the height.
refer to this if you want to consider the keyboard height for all possibilities http://www.idev101.com/code/User_Interface/sizes.html
Dont mess with the contentInset and the scrollIndicatorInsets. Just setting the frameSize will take care of these for you.
this is how your method should be
- (void)keyboardDidShow:(NSNotification*)notification
{
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
CGRect rect = _tableView.frame;
rect.size.height = _tableView.frame.size.height - kbSize.height;
_tableView.frame = rect;
}
- (void)keyboardWillHide:(NSNotification*)notification
{
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
CGRect rect = _tableView.frame;
rect.size.height = _tableView.frame.size.height + kbSize.height;
_tableView.frame = rect;
}
I have used this piece of code for a similar functionality. So if its still not working, there's something else going wrong.
I am curious why this isn't working for you, as I have basically the same thing and it is working for me. There is only one difference that I can see, in that I don't access '_tableView' and instead make sure that I'm always using the getter and setter.
Here is what I do, that is working.
- (void)keyboardDidShow:(NSNotification *)keyboardNotification
{
NSDictionary *info = [keyboardNotification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
CGFloat newBottomInset = 0.0;
UIEdgeInsets contentInsets;
if (UIDeviceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) ) {
newBottomInset = keyboardSize.height;
} else {
newBottomInset = keyboardSize.width;
}
contentInsets = UIEdgeInsetsMake(0.0, 0.0, newBottomInset, 0.0);
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
}
Note that my app allows device rotation and when that happens the value used needs to be the width of the keyboard because the values are relative to the portrait orientation, which caused me hours of confusion.
Hopefully the self.tableView access will make the difference.
Related
I use the following when show/hide the keyboard:
- (void)keyboardWasShown:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrDetails.contentInset = contentInsets;
self.scrDetails.scrollIndicatorInsets = contentInsets;
}
- (void)keyboardWillHide:(NSNotification *)notification
{
/* Set to NO so after the keyboard is hidden, the size goes back to normal */
self.automaticallyAdjustsScrollViewInsets = NO;
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrDetails.contentInset = contentInsets;
self.scrDetails.scrollIndicatorInsets = contentInsets;
[self.view layoutIfNeeded];
}
But for some reason even though the size is set correctly when dismissing the keyboard
(lldb) po self.scrDetails.contentInset
(top = 0, left = 0, bottom = 0, right = 0)
the effect is not shown on the device/simulator(the scrollbar remain at the previous keyboard up position)
See the image
I use autolayout, so I am not setting the contentSize directly.
Any suggestions what might help tracking this down?
You can use third party class TPKeyboardAvoiding. It manage keyboard.
Link : https://github.com/michaeltyson/TPKeyboardAvoiding
I have textViews in a tableView. When textViews (towards the bottom of the tableView) get selected the keyboard pops up, and hides the textView, and you can't scroll down either. So I added the following code:
- (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);
myTableView.contentInset = contentInsets;
myTableView.scrollIndicatorInsets = contentInsets;
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
[self.myTabelView scrollRectToVisible:activeField.frame animated:YES];
}
}
I now have the opposite problem. When the keyboard does show, towrad the top doesn't show, and it's not possible to scroll all the way up either.
Hi and did you try to use TPKeyboardAvoiding ? I use it to solve such cases without to need to code anything... You can try it as CocoaPod quickly.
Am using the below code to get keyboard height which varies in IPhone 5s device with ios8 compared to IPhone4s device with ios7 .As a result my textfield is moving very high when i tap on it in IPhone5s with ios8 while the same code works fine in IPhone 4s with ios7.Can someone guide how the problem can be fixed in both versions.
- (void)keyboardWasShown:(NSNotification*)notification
{
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
float kbHeight;
if (([UIApplication sharedApplication].statusBarOrientation== UIDeviceOrientationPortraitUpsideDown)||([UIApplication sharedApplication].statusBarOrientation== UIDeviceOrientationPortrait))
{
kbHeight=kbSize.height;
}
else
{
kbHeight=kbSize.width;
}
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbHeight, 0.0);
self.scroll.contentInset = contentInsets;
self.scroll.scrollIndicatorInsets = contentInsets;
CGRect rect = self.view.frame;
rect.size.height -= (kbHeight);
if (!CGRectContainsPoint(rect, self.activeField.frame.origin))
{
CGPoint scrollPoint = CGPointMake(0.0, self.activeField.frame.origin.y - ((kbHeight) - self.activeField.frame.size.height));
[self.scroll setContentOffset:scrollPoint animated:NO];
}
}
Simply Replace the line from your code
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
With
CGSize kbSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
Let me know if it works
This is not an issue, you are getting different size keyboard because Predictive is Enable.
The height of keyboard is 216 which is fixed but when Predictive is Enabled you will get 253 as height.
So you have to write code for both conditions.
use this code, may help you
- (void)keyboardWillShow:(NSNotification*)note {
NSDictionary* info = [note userInfo];
CGSize _kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
float kbHeight = _kbSize.width > _kbSize.height ? _kbSize.height : _kbSize.width;
}
kbHeight variable stored height of the keyboard.
I have an iPhone that I am adding landscape support. It was portrait only before.
I am having problems with getting text fields moved out of the way when the keyboard shows.
I am using a solution very similar to Apple's solution documented here.
The solution works just fine when in portrait mode but when I go to landscape mode it's not working at all. The view is not scrolling up but instead scrolls down.
I thought it may have to do with the keyboard hight/width and I think I am accounting for that properly.
Any advice to point out my stupidity or a much better solution is appreciated.
- (void)keyboardWillShow:(NSNotification *)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect activeFieldFrame = activeField.frame;
CGFloat kbHeight;
if (self.interfaceOrientation == UIInterfaceOrientationPortrait)
{
kbHeight = kbSize.height;
}
else
{
kbHeight = kbSize.width;
}
UIEdgeInsets contentInsets;
contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbHeight, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
CGRect aRect = self.view.frame;
aRect.size.height -= kbHeight;
activeFieldFrame.origin.y += scrollView.frame.origin.y+aRect.origin.y;
if (!CGRectContainsRect(aRect, activeFieldFrame) )
{
CGPoint scrollPoint = CGPointMake(0.0, activeFieldFrame.origin.y-kbHeight-aRect.origin.y);
[scrollView setContentOffset:scrollPoint animated:YES];
}
}
Note I am doing this in the keyboard WILL show as I think it looks better to move the view before the keyboard is shown.
OK, I'll answer my own question with my solution. Of course I seem to come across the solution right after I post to SO.
My maths were all wrong.
Here is my updated version in case it helps anyone else.
This will work both landscape and portrait.
- (void)keyboardWillShow:(NSNotification *)aNotification
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect activeFieldFrame = activeField.frame;
CGRect scrollViewFrame = scrollView.frame;//used so I can see values below while debugging
CGFloat kbHeight;
if (self.interfaceOrientation == UIInterfaceOrientationPortrait)
{
kbHeight = kbSize.height;
}
else
{
kbHeight = kbSize.width;
}
UIEdgeInsets contentInsets;
contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbHeight, 0.0);
scrollView.contentInset = contentInsets;
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 -= kbHeight;
activeFieldFrame.origin.y += scrollViewFrame.origin.y+aRect.origin.y;
if (!CGRectContainsRect(aRect, activeFieldFrame) )
{
//add +5 here to give just a little room between field and keyboard
//subtracting scrollviewFrame.origin.y handles cases where scrollview is not at top (0,0) of the view
CGPoint scrollPoint = CGPointMake(0.0, activeFieldFrame.origin.y + activeFieldFrame.size.height + 5 - aRect.size.height - scrollViewFrame.origin.y);
[scrollView setContentOffset:scrollPoint animated:YES];
}
}
}
One interesting thing to note that I ran across is that if you register to get keyboard notifications on view A and pop modal view B over the top of view A and modal view B also needs keyboard shenanigans so you register modal view B to handle them, view A will still get the keyboard event and execute the code even though it is not visible on screen!
The original view A will still get the keyboard will show event if it's part of a tab-based design and another view C that has an edit field which is shown by another tab.
It seems logical that you should unregister listening for keyboard events when your view will disappear and register for events when your view will appear.
Hopefully this helps somebody else.
Scroll views are usually scrolled for you when your text field becomes first responder. I've had times where I haven't wanted this, so far the only workaround I have found was to wrap the text fields in scroll views that are the same size as the field so the auto scrolling behavior effects them and not my main scroll view.
How can I check if the keyboard is covering a first responder inside a UIScrollView which may or may not be a UITableView? Note the UIScrollView will not necessarily cover the entire viewController's view and may be contained in a modal view (UIModalPresentationFormSheet).
I'm using this modified code from Apple's reference documentation and example, but CGRectContainsPoint will return false even when the keyboard is clearly covering the first responder. It's obvious I'm not using convertRect:toView correctly.
Also, Apple's code does not take into account that the view is not full-screen, so setting the scrollView's contentInset to the full height of the keyboard isn't a great solution -- it should only be inset for the portion of the keyboard covering the firstResponder.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
// self.scrollView can be a tableView or not. need to handle both
UIView *firstResponderView = [self.scrollView findFirstResponder];
if (!firstResponderView)
return;
NSDictionary* info = [aNotification userInfo];
CGRect rect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// convertRect:toView? convertRect:fromView? Always confusing
CGRect kbRect = [self.scrollView convertRect:rect toView:nil];
CGRect viewRect = [self.scrollView convertRect:firstResponderView.bounds toView:nil];
// doesn't work. convertRect misuse is certainly to blame
if (!CGRectContainsPoint(kbRect, firstResponderView.frame.origin))
return;
// Only inset to the portion which the keyboard covers?
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbRect.size.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
Without further testing or having a deep look at the logic, this line seems odd:
CGRect kbRect = [self.scrollView convertRect:rect toView:nil];
The keyboard rect (that is included in the notification) is in window coordinates and you probably want to convert it into the scroll view coordinate system. [viewA convertRect:rect toView:viewB] converts rect from viewA's coordinate system to viewB's coordinate system, so you are actually doing the opposite of what you should be doing (as you suspected).
What I'm usually doing is this:
- (void)keyboardWillShow:(NSNotification *)aNotification
{
NSDictionary *info = [aNotification userInfo];
CGRect kbRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
kbRect = [self.view.window convertRect:kbRect toView:self.view]; // convert to local coordinate system, otherwise it is in window coordinates and does not consider interface orientation
_keyboardSize = kbRect.size; // store for later use
[UIView animateWithDuration:0.25 animations:^{
UIEdgeInsets insets = UIEdgeInsetsMake(0.0f, 0.0f, MAX(0.0f, CGRectGetMaxY(_tableView.frame) - CGRectGetMinY(kbRect)), 0.0f); // NB: _tableView is a direct subview of self.view, thus _tableView.frame and kbRect are in the same coordinate system
_tableView.contentInset = insets;
_tableView.scrollIndicatorInsets = insets;
[self scrollToActiveTextField]; // here I adapt the content offset to ensure that the active text field is fully visible
}];
}