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.
Related
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.
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;
}
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){ }];
}
When the keyboard was hidden, the scrollview should back to it's origin contentInset, but it's not working in iOS7. Setting scrollview's contentInset when keyboard was shown is working but when the keyboard was hidden, the scrollview's contentInset can't set to inset zero.
The code:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:Nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasHidden:) name:UIKeyboardDidHideNotification object:nil];
}
- (void)keyboardWasShown:(NSNotification *)notif
{
CGSize keyboardSize = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
UIScrollView *scrollView = (UIScrollView *)self.view;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
CGRect rect = self.view.frame;
rect.size.height -= keyboardSize.height;
if (!CGRectContainsPoint(rect, self.wishContentField.frame.origin)) {
CGPoint point = CGPointMake(0, self.wishContentField.frame.origin.y - keyboardSize.height);
[scrollView setContentOffset:point animated:YES];
}
}
- (void)keyboardWasHidden:(NSNotification *)notif
{
UIEdgeInsets zeroInsets = UIEdgeInsetsZero;
UIScrollView *scrollView = (UIScrollView *)self.view;
[scrollView setContentInset:zeroInsets];
scrollView.scrollIndicatorInsets = zeroInsets;
}
Try this one:
self.automaticallyAdjustsScrollViewInsets = NO;
This is working for me...
it might be related to contentSize not working
except when set in VCs
- (void)viewDidLayoutSubviews
{
self.scrollView.contentSize = whatever
}
just saying you might be smashing your head against the wrong wall
So, just because I still found this answer useful, here is what I did. I took the advice of #alex-i and the comment by #yong-ho. But for some reason the height of the navigation bar wasn't quite enough.
UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.navigationController.navigationBar.frame.size.height + 20.0f, 0.0, 0.0, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
Like I said, I had to add that 20.0f or my content was still cut off. Not sure why. If I figure it out I'll update my answer.
Set contentOffset to zero. It will work in all cases no matter your scroll view is inside navigation controller or any other. Find below the code snippet for same:
- (void)keyboardWasHidden:(NSNotification *)notif
{
UIScrollView *scrollView = (UIScrollView *)self.view;
scrollView.contentOffset = CGPoint.zero
}
I also found that if you set a new contentinset exactly the same as the existing inset, the scrollview may ignore it and revert to a zero inset. So an easy workaround is to check that the new contentinset you are setting is at least 1 point different than the last.
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