Keyboard pushes UIScrollView content out of screen - ios

I've been struggling with this keyboard and scrollview issue for quite sometime now. I'm trying to make a chat room similar to What'sApp and iMessage. I have UITabBar as a root view controller. For the chat room view I have a toolbar at the bottom that contains UITextView and UIButton the issue is that when the keyboard is presented it pushes the content view out of the screen and I can't see about 1/5 of the top of the content view. I tried playing with the numbers and still can't get it to work properly. Any help would be greatly appreciated.
- (void)keyboardWasShown:(NSNotification *) aNotification {
NSDictionary *info = [aNotification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
// the hardcoded 49 is the height of the UITabBar at the bottom below the input toolbar
UIEdgeInsets contentInsets = UIEdgeInsetsMake((-keyboardSize.height+49), 0.0, (keyboardSize.height-49), 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 app might not need or want this behavior.
// CGRect aaRect = self.view.frame;
// aaRect.size.height -= keyboardSize.height;
// if (!CGRectContainsPoint(aaRect, self.activeTextView.frame.origin)) {
// [self.scrollView scrollRectToVisible:self.activeTextView.frame animated:NO];
// }
CGPoint scrollPoint = CGPointMake(0, self.scrollView.contentInset.bottom);
[self.scrollView setContentOffset:scrollPoint animated:true];
[self.view addGestureRecognizer:self.tapRecognizer];
}
- (void)keyboardWillBeHidden:(NSNotification *) aNotification {
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
[self.view removeGestureRecognizer:self.tapRecognizer];
}

I met the same problem long time ago. My solution is to listen keyboard frame did change notification(because different keyboard has different frame). And I think it is easier to adjust the frame of scroll view rather than content offset.

Related

UIScrollview loses its top contents when I move the containing view up with the keyboard ios

I have been pulling my hair out trying to fix this problem the last few weeks. I move up the entire view contained in my view controller when the keyboard appears. Once I do so the top portion of my scrollview contained in the view becomes unreachable. It is like the top of the screen is cutting off the top portion of my scrollview. Is there some way to fix this?
//Move the keyboard when you select a textfield.
-(void)keyboardWillShow:(NSNotification*)notification{
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect viewRect = self.view.frame;
viewRect.origin.y = -215;
[UIView animateWithDuration:0.3f animations:^ {
self.view.frame = viewRect;
}];
scrollBounces = YES;
scrollView.contentOffset = CGPointZero;
}
This is all that I am doing to the view that contains the scrollview.
You can set the contentInset and scrollIndicatorInsets for the scrollView too:
[scrollView setContentInset:UIEdgeInsetsMake(215, 0, 0, 0)];
[scrollView setScrollIndicatorInsets:UIEdgeInsetsMake(215, 0, 0, 0)];
Write the below three lines in the keyboardWillShow
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kKeyboardHeightValue, 0.0);
self.mDisplayedScrollview_.contentInset = contentInsets;
self.mDisplayedScrollview_.scrollIndicatorInsets = contentInsets;
Write the below three lines in the keyboardWillHide
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.mDisplayedScrollview_.contentInset = contentInsets;
self.mDisplayedScrollview_.scrollIndicatorInsets = contentInsets;

Using Autolayout with UIView embedded in UIScrollView

I have a View embedded inside a Scroll View. I have written code such that when the cursor is placed in a text field (within the View) that is hidden by the keyboard that the screen will readjust such that the text field is no longer hidden. I am using autolayout to do this.
My problem is that when I put the View in the View Controller at the position I want it, there is a lot of additional white space above when I run the simulator. But when the keyboard comes up (and the screen redraws) then I can scroll up and the View Controller no longer has that additional white space.
If I adjust the view so that there isn't any white space (so it appears as on the storyboard) then when the keyboard appears, I cannot scroll to the top of the View.
I assume this has something to do with how the View is centered within the Scroll View but I have tried numerous adjustments on the constraints in the Size Inspector and cannot resolve this. I would really like to fix this in the storyboard (versus through code) if at all possible...
Here is the code:
- (void) keyboardDidShow:(NSNotification *)notification
{
NSDictionary* info = [notification userInfo];
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
kbRect = [self.view convertRect:kbRect fromView:nil];
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbRect.size.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
CGRect aRect = self.view.frame;
aRect.size.height -= kbRect.size.height;
if (!CGRectContainsPoint(aRect, self.activeField.frame.origin) ) {
[self.scrollView scrollRectToVisible:self.activeField.frame animated:YES];
}
}
- (void) keyboardWillBeHidden:(NSNotification *)notification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
Turns out that I was setting the EdgeInsets value too high... when I changed from
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbRect.size.height, 0.0);
to
UIEdgeInsets contentInsets = UIEdgeInsetsMake(60.0, 0.0, kbRect.size.height, 0.0);
The positioning was improved.

iOS: scrollView not scrolling to correct position when keyboard visible

I am testing my iOS app using a 6.1 simulator. I have been working for hours to scroll my scrollView to the correct position when a keyboard is visible (after a user clicks on a textView). I have tried following the answer marked as correct on this page:
How do I scroll the UIScrollView when the keyboard appears?
This is what I currently have:
- (void)keyboardWasShown:(NSNotification*)aNotification {
NSLog(#"keyboardWasShown");
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 app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.activeView.frame.origin) ) {
NSLog(#"scrollToView");
CGPoint scrollPoint = CGPointMake(0.0, self.stepDescriptionField.frame.origin.y-kbSize.height);
NSLog(#"scrollPoint: %f", scrollPoint.y);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}
You can see from the images above that if the user clicks on the textView, the scrollView is not scrolled to the correct position (you should be able to see the text contents of the textView).
The strange thing is that I manually tried changing the y-offset of the scrollPoint to different values, but it seemed to have no effect on where the window scrolls to. What am I doing wrong?
Other things that may be important:
I have autolayout turned off (so that the user can scroll vertically in this view).
the textView is not scrollable (it is resized to fit its contents)
Edit
I found that if I add my offset to the contentInsets as follows:
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height+50.0, 0.0);
the view will scroll to the correct position. The only downside is that there is extra padding at the bottom:
Is there a better way to do this?
I used this with a UITextField and not a UITextView but i believe it should still work the same. This allows me to positions the textfield directly above the keyboard.
keyboardWillShow is the function when NSNotificationCenter receives UIKeyboardWillShowNotification
-(void) keyboardWillShow:(NSNotification *)note
{
// Get the keyboard size
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];
// Start animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3f];
// Get Keyboard height and subtract the screen height by the origin of the textbox and height of text box to position textbox right above keyboard
self.scrollView.contentOffset = CGPointMake(0,keyboardBounds.size.height-([UIScreen mainScreen].bounds.size.height - commentBox.frame.origin.y - commentBox.frame.size.height));
[UIView commitAnimations];
}

iOS landscape view moving text fields when keyboard shows

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.

scroll UISearchBar in a toolbar when Keyboard is appearing in iPad App?

I read many topics but I cannot find a solution :-( maybe you can help!
In my storyboard (for iPad App) I have the following:
- a Navigation Controller that is my initial Scene
- a Segue that opens a View Controller that contains the following hierarchy:
- Scroll View
-> Toolbar
-> BAR Button Item
-> Search Bar With Prompt
-> Map View
- Navigation Item
-> Bar Button item
My Search Bar is in the bottom toolbar and as a consequence when I click on it the keyboard is displayed and it hides my Search Bar. I would like to scroll up everything to display my Search Bar (Navigation, Map, Toolbar...).
I have registered the Keyboard notifications and implemented the code as described in the IOS documentation
- (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);
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 -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
}
When this code is executed, it seems something happens (My map view display a white view instead of the map) and the keyboard is displayed but my Search bar is still invisible.
Is there a issue in my View hierarchy? because it's embedded in a Navigation controller? I'm a little bit lost for this issue. Just a point, before to have a scrollView in my hierarchy I had a simple View, just replaced the simple view with the scroll view because of the issue with the Search Bar.
Your helps will be very appreciated!
Thanks,
Sébastien.
I found my mistake, it's because my App is in Landscape mode and the uiSearchBar position is relative to the toolbar.
Below is the fix
- (void)keyboardWasShown:(NSNotification *)aNotification {
// Get the Keyboard size
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// Add to the scroll view a bottom inset equal to height of the keyboard
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.width; // in landscape mode need to get width and not height!
// Compute the position of the uiSearchBar in the screen (whole scrollview)
CGPoint realPoint = [_LocateCell convertPoint:_LocateCell.frame.origin toView:self.scrollView];
if (!CGRectContainsPoint(aRect, realPoint) ) {
CGPoint scrollPoint = CGPointMake(0.0, kbSize.width);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}

Resources