iOS: Hidden parts of table view behind keyboard issue - ios

I've been struggling with this for hours now and worked through a lot on stackoverflow and the docs. However I cannot get the apple code running properly in my app.
Here's the complete apple code and from there I afterwards I ask my questions:
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (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 app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
[self.scrollView scrollRectToVisible:activeField.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
1.: Why do they adjust the insets here? I still don't get that:
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
2: I'm writing with this code a subclass of UITableView in order to use it in all my table views in my app. I never have just a plain table view, usually it's embedded in a superview. Or let's say I usually have a view at the top of my screen, one at the bottom and in the middle between I have my tableview. I want to move my table view up only if a textfield is hidden below the keyboard and only so much that this textfield is visible. I'm saying that because at least in one screen with the keyboard shown only about 2 cells at once are visible. And with the apple code the table view get's scrolled up waaaay to much. I've adjusted the code so far like this (my subclass of tableview has two properties, activeField and activeFieldOriginInSuperView (without that I think it's not possible?) But it's moving the table view still way too much up and the relevant textfield isn't visible anymore...any idea what's wrong?
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize keyBoardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect tableFrame = self.frame;
tableFrame.size.height -= keyBoardSize.height;
CGPoint activeFieldOrigin = self.activeFieldOriginInSuperView;
activeFieldOrigin.y -= self.contentOffset.y;
CGPoint activeFieldLowerEnd = CGPointMake(activeFieldOrigin.x, activeFieldOrigin.y + self.activeField.frame.size.height + 5);
if (!CGRectContainsPoint(tableFrame, activeFieldLowerEnd) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeFieldLowerEnd.y - tableFrame.size.height);
[self setContentOffset:scrollPoint animated:YES];
}
}

It will definitely work. It works for me for last 50 applications.
Try this
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHide:) name:UIKeyboardWillHideNotification object:nil];
- (void)keyboardDidShow: (NSNotification *) notif{
[UIView animateWithDuration:0.5f delay:0.0f options: UIViewAnimationOptionCurveEaseInOut animations:^{
_scrollView.contentOffset = CGPointMake(0, txtId_.frame.origin.y - 200);
} completion:^(BOOL finished){ }];
}
- (void)keyboardDidHide: (NSNotification *) notif{
[UIView animateWithDuration:0.5f delay:0.0f options: UIViewAnimationOptionCurveEaseInOut animations:^{
_scrollView.contentOffset = self.view.frame.origin;
} completion:^(BOOL finished){ }];
}

Related

Move visibility of UIScrollView area automatically

I want to make my view automatically moving up if keyboard appears. Already use apple's code here and it works well.
This is how I manage my object, so I create a UIScrollView that covers UIView. This UIView consists of UITextField and UIButton.
This is how I adjust my view when keyboard appears.
#pragma mark - Keyboard Handling
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (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 app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, _mainView.frame.origin) ) {
[self.scrollView scrollRectToVisible:_mainView.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
_scrollView.contentInset = contentInsets;
_scrollView.scrollIndicatorInsets = contentInsets;
}
But I think there is a point that make that weird. When keyboard showed up, it scrolls and my UITextField become visible. But I thought it was too tight.
In my opinion, it would be better if my UITextField moving up a little bit. My question is, how can I set its scroll visibility? It looks like some variable should be added with some constant here
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, _mainView.frame.origin) ) {
[self.scrollView scrollRectToVisible:_mainView.frame animated:YES];
}
Note:
Result that I wanted
Thank you so much, a little hint would be appreciated.
The simplest solution would be to move your view (or scrollview) up when keyboard opens.
- (void)keyboardWillShow:(NSNotification*)notification{
[self.view setFrame:CGRectMake(0,-100, self.view.frame.size.width, self.view.frame.size.height)]; // where 100 is the offset
[self.view setNeedsDisplay];
}
- (void)keyBoardWillHide:(NSNotification*)notification{
[self.view setFrame:CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height)];
[self.view setNeedsDisplay];
}
Solved
I solved this by manage its content inset by adding some number.
In keyboardWasShown:, I added its content inset by height of my textfield and button. Let say it was 100 total, so this is it.
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height+100, 0.0);
Thank you so much.

Moving UIView on top of Keyboard with AutoLayout

I have a chat view with UITableView on all of the screen and on the bottom of the screen there is a UIView that holds 2 UIButtons and UITextField.
What I want to do is when the user clicks on the UITextField and the keyboard shows, the view will change his location to the top of the keyboard. For some reason I think that the AutoLayout is preventing it from moving, how could I know?
This is what I did so far:
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(kbSize.height, 0.0, kbSize.height, 0.0);
self.tableChat.contentInset = contentInsets;
self.tableChat.scrollIndicatorInsets = contentInsets;
CGRect inputTextView = self.accessoryViewForChat.frame;
inputTextView.origin.y = inputTextView.origin.y - kbSize.height;
self.accessoryViewForChat.frame = inputTextView;
// 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.activeTextField.frame.origin) ) {
[self.tableChat scrollRectToVisible:self.activeTextField.frame animated:NO];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.tableChat.contentInset = contentInsets;
self.tableChat.scrollIndicatorInsets = contentInsets;
CGRect inputTextView = self.accessoryViewForChat.frame;
inputTextView.origin.y = inputTextView.origin.y + kbSize.height;
self.accessoryViewForChat.frame = inputTextView;
}
#pragma mark UITextFieldDelegate methods
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
self.activeTextField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
self.activeTextField = nil;
}

make iOS page scrollable when keyboard is enabled

I have an iOS page for register and I want to make it scrollable if the keyboard is enabled, because at the moment I can't scroll to the register button at the end of the page and the keyboard covert the button.
Is there any smart solution?
For solving of you're problem are quite a lot of solutions. From using UIScrolView to changing frame or constrain.
If you would like to use UIScrolView you should insert you register form UIView to UIScrolView and set content size.
Code sniper how to handle keyboard and scrollview.
First of all you should know when keyboard is shown and hide. Use notifications for that :
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
After that use methods (keyboardWasShown and keyboardWillBeHidden) of notifications to change contentInsets.
Example of changing of the contentInsets :
- (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;
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
Finally solution is depends from you're choice you can change frame or constrain as well like UIScrolView parameters.

contentInset being ignored in ios7 for UIScrollView

This worked before ios7 when someone tapped on anything that could become first responder inside a UIScrollView. Now it does not - UITextFields/Views still can show under the keyboard.
Code:
- (void)keyboardWasShown:(NSNotification*)notification{
//Some similar questions mentioned this might work, but made no difference for me
self.automaticallyAdjustsScrollViewInsets=NO;
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
float height = 0.0;
if (UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
height = kbSize.width;
} else {
height = kbSize.height;
}
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, height, 0.0);
[UIView animateWithDuration:.25
delay:0
options:(UIViewAnimationOptionAllowUserInteraction)
animations:^
{
self.editorScrollView.contentInset = contentInsets;
self.editorScrollView.scrollIndicatorInsets = contentInsets;
}
completion:^(BOOL finished)
{
}];
}
Currently, with this code nothing takes place when a uitextfield/view is assigned first responder status. The insets don't seem to change - I perhaps could use contentOffset but I would have to find the origin view's Y who just become first responder to do that.
Like I said, before ios7 this code worked (no textfield/view would be hidden behind the keyboard when assigned first responder status). I seem to be missing something obvious or perhaps there is a better way of doing this in ios7?
A better way to detect keyboard changing and frame.
The key point is to convert keyboard frame: CGRect keyboardFrameInsideView = [self.view convertRect:keyboardFrame fromView:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardFrameWillChange:)
name:UIKeyboardWillShowNotification
object:nil];
- (void)keyboardFrameWillChange:(NSNotification *)notification
{
CGRect keyboardFrame;
[[notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrame];
CGRect keyboardFrameInsideView = [self.view convertRect:keyboardFrame fromView:nil];
CGRect r = self.bodyView.frame;
r.size.height = CGRectGetMinY(keyboardFrameInsideView) - r.origin.y;
self.bodyView.frame = r;
}

UIScrollView Content Insets not working for Keyboard Height

I am trying to move a UIScrollView when the keyboard hides a UITextField by changing the size using the contentInsets as it is shown.
However, it's not working for the keyboard height. The keyboard height comes as 216, but it only stops scrolling at the correct location if I set the bottom inset to 515 for iPhone portrait mode and 310 for iPhone landscape mode. Why would these dimensions be so different? I don't want to hardcode these arbitrary values in.
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.frame = self.parentViewController.view.frame;
[self.textView becomeFirstResponder];
NSLog(#"scrollview: %f,%f, parent: %f,%f", self.view.frame.size.height, self.view.frame.size.width, self.parentViewController.view.frame.size.height, self.parentViewController.view.frame.size.width);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWasShown:(NSNotification *)notification
{
if([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPhone) {
// Step 1: Get the size of the keyboard.
CGFloat keyboardHeight = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;
// Step 2: Adjust the bottom content inset of your scroll view by the keyboard height.
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
((UIScrollView*)self.view).contentInset = contentInsets;
((UIScrollView*)self.view).scrollIndicatorInsets = contentInsets;
// Step 3: Scroll the target text field into view.
CGRect aRect = self.view.frame;
aRect.size.height = aRect.size.height - keyboardHeight;
if (!CGRectContainsPoint(aRect, self.textView.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, self.textView.frame.origin.y - keyboardHeight);
[((UIScrollView*)self.view) setContentOffset:scrollPoint animated:YES];
}
}
}
- (void) keyboardWillHide:(NSNotification *)notification {
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
((UIScrollView*)self.view).contentInset = contentInsets;
((UIScrollView*)self.view).scrollIndicatorInsets = contentInsets;
}
Edit:
Before the keyboard is open, i print this out:
NSLog(#"scrollview: %f,%f, parent: %f,%f", self.view.frame.size.height, self.view.frame.size.width, self.parentViewController.view.frame.size.height, self.parentViewController.view.frame.size.width);
and it prints this:
scrollview: 431.000000,320.000000, parent: 431.000000,320.000000
I think that the main issue is that you need to use UIKeyboardFrameEndUserInfoKey instead of UIKeyboardFrameBeginUserInfoKey.
That being said, I have written a different version of this method that will work even if the scrollview is not placed at the bottom of the screen and may work better for you:
Note that I use self.activeTextField instead of self.textField.
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
// Calculate the frame of the scrollview, in self.view's coordinate system
UIScrollView *scrollView = self.scrollView;
CGRect scrollViewRect = [self.view convertRect:scrollView.frame fromView:scrollView.superview];
// Calculate the frame of the keyboard, in self.view's coordinate system
NSDictionary* info = [aNotification userInfo];
CGRect kbRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
kbRect = [self.view convertRect:kbRect fromView:nil];
// Figure out where the two frames overlap, and set the content offset of the scrollview appropriately
CGRect hiddenScrollViewRect = CGRectIntersection(scrollViewRect, kbRect);
if (!CGRectIsNull(hiddenScrollViewRect))
{
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, hiddenScrollViewRect.size.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
[self scrollToActiveTextField];
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
self.scrollView.contentInset = UIEdgeInsetsZero;
self.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
- (void)scrollToActiveTextField
{
if (self.activeTextField)
{
CGRect visibleRect = self.activeTextField.frame;
visibleRect = [self.scrollView convertRect:visibleRect fromView:self.activeTextField.superview];
visibleRect = CGRectInset(visibleRect, 0.0f, -5.0f);
[self.scrollView scrollRectToVisible:visibleRect animated:YES];
}
}
set delegate of your scrollView as self whereever you are crating scrollview in your code
((UIScrollView*)self.view).delegate=self;
then
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.frame = self.parentViewController.view.frame;
[self.textView becomeFirstResponder];
NSLog(#"scrollview: %f,%f, parent: %f,%f", self.view.frame.size.height, self.view.frame.size.width, self.parentViewController.view.frame.size.height, self.parentViewController.view.frame.size.width);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWasShown:(NSNotification *)notification
{
if([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPhone) {
// Step 1: Get the size of the keyboard.
CGFloat keyboardHeight = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;
// Step 2: Adjust the bottom content inset of your scroll view by the keyboard height.
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
((UIScrollView*)self.view).contentInset = contentInsets;
((UIScrollView*)self.view).scrollIndicatorInsets = contentInsets;
((UIScrollView*)self.view).scrollEnabled=YES;
}
}
- (void) keyboardWillHide:(NSNotification *)notification {
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
((UIScrollView*)self.view).contentInset = contentInsets;
((UIScrollView*)self.view).scrollIndicatorInsets = contentInsets;
[((UIScrollView*)self.view) setContentOffset:CGPointMake(0.0f, 0.0f) animated:TRUE];
((UIScrollView*)self.view).contentSize = CGSizeMake(((UIScrollView*)self.view).contentSize.width, ((UIScrollView*)self.view).contentSize.height);
((UIScrollView*)self.view).scrollEnabled=NO;
}
#pragma mark - set scrollView content position
-(void)scrollViewToCenterOfScreen:(UIView *)theView
{
CGFloat theViewY = theView.center.y;
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
CGFloat avaliableHeight = applicationFrame.size.height - 300;
CGFloat y = theViewY - avaliableHeight / 2.0;
if(y<0)
y = 0;
[((UIScrollView*)self.view) setContentOffset:CGPointMake(0,y) animated:YES];
}
#pragma -mark UITextField Delegate methods
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
[self scrollViewToCenterOfScreen:textField];
return YES;
}
Now set delegate of your text field. Whenever textFieldShouldBeginEditing: your scrollview automatically move on that. scrollViewToCenterOfScreen: method set your scrollview position at textfiled position.
It definitely set your scrollview content insets as well as setContentOffset. Please let me know if you still facing this problem. Thanks

Resources