Scroll tableview row up when keyboard appears iOS - ios

My apps some ui components hides from bottom when keyboard appears, in fact its a tableView's last row.
I am using NSNotificationCenter to notify when keyboard appears and disappears.
Here is my code:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardOnScreen:) name:UIKeyboardDidShowNotification object:nil];
-(void)keyboardOnScreen:(NSNotification*)notification{
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect screen = self.view.frame;
screen.size.height -= kbSize.height;
float HeightOfToolbar =_inputbar.frame.size.height; //TOOLBAR
float yPoint = screen.size.height-HeightOfToolbar;
CGPoint scrollPt = CGPointMake(0, yPoint);
[_dataTable setContentOffset:scrollPt animated:YES];
}
However, tableview scroll up but not at correct row i.e last row above keyboard.
Sometimes it scroll to second last row hiding last row, or more above.
its related to scrollPt value!

Use this method keyboard appears
- (void)keyboardWasShown:(NSNotification *)aNotification {
NSDictionary* info = [aNotification userInfo];
CGSize Size = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, Size.height - HeightOfToolbar, 0.0);//Set height according to key board height
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
Hide key board
- (void)keyboardWasHidden:(NSNotification *)aNotification
{
self.tableView.contentInset = UIEdgeInsetsZero;
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

Related

ScrollView's scrollRectToVisible on keyboard notifications Not working properly in iOS 11

I have a scroll view with many text fields as subviews, I wanted the selected text field(_activeField) to scroll up when keyboard appears.
- (void) keyboardUP:(NSNotification *)notification
{
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height + 10, 0.0);
_scroller.contentInset = contentInsets;
_scroller.scrollIndicatorInsets = contentInsets;
CGRect aRect = App_Delegate.window.frame;
aRect.size.height -= kbSize.height;
CGRect rect = [_activeField convertRect:_activeField.frame fromView:App_Delegate.window];
if (!CGRectContainsPoint(aRect, rect.origin) )
{
[_scroller scrollRectToVisible:rect animated:YES];
}
}
This code is working fine for iOS 10, but in iOS 11 , its not working ,"scrollRectToVisible" doesn't scroll the ScrollView even if its content size is correct.
I am not sure about
- (void) keyboardUP:(NSNotification *)notification
but have seen a similar problem with
- (void)handleKeyobardDidShowNotification:(NSNotification *)notification
It looks like this no longer runs on the main thread in iOS 11.
Try running you call to scrollRectToVisible on the main thread as follows
dispatch_async(dispatch_get_main_queue(), ^{
[_scroller scrollRectToVisible:targetPosition animated:YES];
});
This is a duplicate of this issue.
use the UIKeyboardFrameEndUserInfoKey
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
print(keyboardSize)
}
}
and for ObjC
- (void)keyboardWillShow:(NSNotification *)notification {
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
}

IOS: UITextField+UITableView inside UIScrollView

I am new in iOS. I am making a chat application so I have a UITextField below the UITableView and UITextField and UITableView are inside the UIScrollView (I have disable the scroll in scrollview by self.scrollView.scrollEnabled = NO;).
When I tap on UITextField, the keyboard was shown, so I move the UITextField to above keyboard follow Managing the Keyboard doc
// 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);
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.activeField.frame.origin) ) {
[self.scrollView scrollRectToVisible:self.activeField.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
self.activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
self.activeField = nil;
}
With this code, my TextField move to above keyboard successful
But the problem is: In the TableView, when the keyboard was shown I cannot scroll to see some items on top (about 2 items).
For example, if my tableview have 10items, when the keyboard was shown, i can only see 8items, 2 rest items still exist but I cannot scroll to them
Here is my Demo Project
I think maybe the content inset of UITableView is wrong but I don't know how to fix it.
Any help would be great appreciated
Use Autolayout insteads of contentInsets
In ViewController.h
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *botConstraint;
In ViewController.m
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
self.botConstraint.constant = kbSize.height;
[self.view layoutIfNeeded];
//...
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
self.botConstraint.constant = 0.0f;
[self.view layoutIfNeeded];
//...
}
botConstraint is UITextField/UITableView/UIScrollView .bottom = fatherView.bottom in your Main.storyboard

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;
}

iOS: Hidden parts of table view behind keyboard issue

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){ }];
}

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