For the constant UIKeyboardFrameEndUserInfoKey, in the Apple docs it says:
These coordinates do not take into account any rotation factors
applied to the window’s contents as a result of interface orientation
changes. Thus, you may need to convert the rectangle to window
coordinates (using the convertRect:fromWindow: method) or to view
coordinates (using the convertRect:fromView: method) before using it.
So if I use [view1 convertRect:rect fromView:view2]
What would I insert for the above parameters to get it to convert the rotation values correctly? ie:
view1 = ?
rect = ? (the keyboard frame I'm assuming)
view2 = ?
Been trying some things and getting some funny stuff.
The first view should be your view. The second view should be nil, meaning window/screen coordinates. Thus:
NSDictionary* d = [notification userInfo];
CGRect r = [d[UIKeyboardFrameEndUserInfoKey] CGRectValue];
r = [myView convertRect:r fromView:nil];
Now you have the rect that the keyboard will occupy, in terms of your view. If your view is the current view controller's view (or a subview thereof), rotation and so forth are now accounted for.
I tried the accepted answer and found that it does not actually provide the CGRect of the keyboard within the view. I found that I have to convert the CGRect from the UIScreen object to the UIWindow object, and from the UIWindow object to the UIView object:
NSValue * keyboardEndFrame;
CGRect screenRect;
CGRect windowRect;
CGRect viewRect;
// determine's keyboard height
screenRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
windowRect = [self.view.window convertRect:screenRect fromWindow:nil];
viewRect = [self.view convertRect:windowRect fromView:nil];
I use the above to resize the root view to not be hidden by the keyboard:
NSTimeInterval duration;
CGRect frame;
// determine length of animation
duration = [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// resize the view
frame = self.view.frame;
frame.size.height -= viewRect.size.height;
// animate view resize with the keyboard movement
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:duration];
self.view.frame = frame;
[UIView commitAnimations];
+ (void)parseKeyboardNotification:(NSNotification *)notification
inRelationToView:(UIView *)view
info:(void(^)(NSTimeInterval keyboardAnimationDuration, CGRect keyboardFrameInView, UIViewAnimationOptions keyboardAnimationOptions))callback
{
NSParameterAssert(notification != nil);
NSParameterAssert(view != nil);
NSDictionary *userInfo = [notification userInfo];
UIViewAnimationCurve animationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
UIViewAnimationOptions animationOption = animationCurve << 16; // https://devforums.apple.com/message/878410#878410
NSTimeInterval animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// http://stackoverflow.com/a/16615391/202451
CGRect screenRect = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect windowRect = [view.window convertRect:screenRect fromWindow:nil];
CGRect viewRect = [view convertRect:windowRect fromView:nil];
callback(animationDuration, viewRect, animationOption);
}
Can be used like this
- (void)keyboardWillShowOrHide:(NSNotification *)notification
{
[AGKeyboardInfo parseKeyboardNotification:notification inRelationToView:self.view info:^(NSTimeInterval keyboardAnimationDuration, CGRect keyboardFrameInView, UIViewAnimationOptions keyboardAnimationOptions) {
[UIView animateWithDuration:keyboardAnimationDuration delay:0 options:keyboardAnimationOptions animations:^{
// do any modifications to your views
} completion:nil];
}];
}
Related
How to get a keyboard size to resize UITextView and how to use UIKeyboardFrameEndUserInfoKey with Japanese keyboard? The following code to resize UITextView works good on a standard keyboard. But doesn't work with Japanese. How to fix it?
- (void)keyboardWillShow:(NSNotification *)aNotification {
[self moveTextViewForKeyboard:aNotification up:YES];
}
- (void)keyboardWillHide:(NSNotification *)aNotification {
[self moveTextViewForKeyboard:aNotification up:NO];
}
- (void) moveTextViewForKeyboard:(NSNotification*)aNotification up: (BOOL) up
{
NSDictionary* userInfo = [aNotification userInfo];
// Get animation info from userInfo
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
// Animate up or down
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
CGRect newFrame = textView.frame;
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
newFrame.size.height -= keyboardFrame.size.height * (up? 1 : -1);
textView.frame = newFrame;
[UIView commitAnimations];
}
Thanks a lot for the help!
The correct fragment of the code:
CGRect newFrame = editSource.frame;
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
keyboardFrame.size.height -= tabBarController.tabBar.frame.size.height;
if (up) {
editHeight = newFrame.size.height;
newFrame.size.height -= keyboardFrame.size.height;
} else {
newFrame.size.height = editHeight;
}
editSource.frame = newFrame;
WARNIGN!
The method is obsolete. The correct answer is located here: How can I add support for Chinese keyboard with UITextView on iOS 7?
I have a UITableView with custom cells. Based on an answer to this question I am resizing the view when to accommodate the keyboard and resizing when the keyboard is dismissed. After dismissing the keyboard the table view no longer scrolls.
These are the methods called when showing and hiding the keyboard:
-(void)keyboardWillShow:(NSNotification *)note
{
NSDictionary* userInfo = [note userInfo];
NSValue* keyboardFrameValue = [userInfo objectForKey:#"UIKeyboardBoundsUserInfoKey"];
if (!keyboardFrameValue) {
keyboardFrameValue = [userInfo objectForKey:#"UIKeyboardFrameEndUserInfoKey"];
}
// Reduce the tableView height by the part of the keyboard that actually covers the tableView
CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
CGRect viewRectAbsolute = [myTableView convertRect:myTableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
CGRect frame = myTableView.frame;
frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
myTableView.frame = frame;
[UIView commitAnimations];
UITableViewCell *textFieldCell = (id)((UITextField *)self.textFieldBeingEdited).superview.superview;
NSIndexPath *textFieldIndexPath = [myTableView indexPathForCell:textFieldCell];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[myTableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
-(void)keyboardWillHide:(NSNotification *)note
{
CGRect keyboardRect = [[[note userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
NSTimeInterval animationDuration = [[[note userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect frame = self.myTableView.frame;
frame.size.height += keyboardRect.size.height + 49;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.myTableView.frame = frame;
[UIView commitAnimations];
myTableView.scrollEnabled = YES;
}
Any ideas what I am missing?
I've used the same question you link to to solve the same problem. It's been working great for me although I don't remember how much of the original code I ended up using.
For your problem, (though I imagine you've tried this) the first thing that comes to mind is to look in your code to see if your doing
self.tableView.scrollEnabled = NO;
and if so you should verify that you have a corresponding statement somewhere sets it back to YES; in fact you might set scrollEnabled to YES in keyboardWillHide just to test if that helps.
The problematic line is:
frame.size.height += keyboardRect.size.height + 49;
it should be:
frame.size.height += keyboardRect.size.height - self.navigationController.toolbar.frame.size.height;
am using scrollview for text field when i press that text field the view scrolling down can anyone pls help me why its happening ?
This code am using for scroll
- (void)keyboardWasShown:(NSNotification*)aNotification {
if (keyboardShown)
return;
//static const float deviceHeight = 480;
static const float keyboardHeight = 216;
static const float gap = 5; //gap between the top of keyboard and the control
//Find the controls absolute position in the 320*480 window - it could be nested in other views
CGPoint absolute = [activeField.superview convertPoint:activeField.frame.origin toView:nil];
//NSLog(#"Keyborad Shown %f : %f",absolute.y,(keyboardHeight + gap);
if (absolute.y > (keyboardHeight + gap)) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3f]; //this is speed of keyboard
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = activeField.superview.frame;
bkgndRect.size.height += kbSize.height;
[activeField.superview setFrame:bkgndRect];
[scrollview setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-92) animated:YES];
[UIView commitAnimations];
}
keyboardShown = YES;
}
// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification {
//[UIView beginAnimations:nil context:nil];
//[UIView setAnimationDuration:0.003f]; //this is speed of keyboard
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Reset the height of the scroll view to its original value
CGRect viewFrame = [scrollview frame];
viewFrame.size.height += keyboardSize.height;
scrollview.frame = viewFrame;
//[UIView commitAnimations];
keyboardShown = NO;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
activeField = textField;
}
Try This....
This code my be helpful for you to scroll your scrollview up.
(BOOL)textViewShouldBeginEditing:(UITextView *)textView {
[scrView setContentOffset:CGPointMake(0, textView.frame.origin.y - 20)];
return YES;
}
I have a UIScrollView designed to enable scrolling when the keyboard is up. The positioning of the scroll view is fine in portrait mode; I have positioned it in Interface Builder, and set the contentSize in my code ...
CGSize scrollContentSize = CGSizeMake(360, 221);
self.scrollView.contentSize = scrollContentSize;
However, I am struggling to work out how to change the position of the scrollview so it works effectively when the orientation changes to landscape (and back). Do I somehow need to position it in Interface Builder for landscape(?), and then change the content size in my code dependent on the orientation?
Edit:
Im getting the keyboard size as follows, and changing the content size. This works when i move to Portrait, but the frame is positioned slightly wrong in landscape mode.
(void)keyboardWillShow:(NSNotification *)n{
if (keyboardIsShown) {
return;
}
NSDictionary* userInfo = [n userInfo];
CGRect _keyboardEndFrame;
[[userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&_keyboardEndFrame];
CGFloat _keyboardHeight;
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown) {
_keyboardHeight = _keyboardEndFrame.size.height;
}
else {
_keyboardHeight = _keyboardEndFrame.size.width;
}
// resize the noteView
CGRect viewFrame = self.scrollView.frame;
viewFrame.size.height -= _keyboardHeight;
if (UIDeviceOrientationIsPortrait(orientation)){
CGSize scrollContentSize = CGSizeMake(360, 221);
self.scrollView.contentSize = scrollContentSize;
}
else if (UIDeviceOrientationIsLandscape(orientation)){
CGSize scrollContentSize = CGSizeMake(520, 275);
self.scrollView.contentSize = scrollContentSize;
}
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3];
[self.scrollView setFrame:viewFrame];
[UIView commitAnimations];
keyboardIsShown = YES;
}
What you want to do is figure out the where the top of the keyboard is relative to the view that contains your scrollView. Fortunately, there is a function in UIView called convertRect:fromView: that will take the frame from one coordinate system and map it to another coordinate system. In your case, you know the frame of the keyboard relative to the main window, but would like to know where the top of the keyboard is relative to the view that contains your scrollView. Once you know where the top of the keyboard intersects with your view, you can modify the height of your scrollView to accommodate.
Below is a snippet of code I use, which I found somewhere online and don't take credit for. In your view controller you want to be notified of keyboard show and hide events:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name: UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name: UIKeyboardWillHideNotification object:nil];
Then implement keyboardWillShow: and keyboardWillHide: like this:
// When the keyboard shows resize self.textView to touch the top of the keyboard.
-(void)keyboardWillShow:(NSNotification *)_notification {
NSDictionary *userInfo = [_notification userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
// Here we figure out where the keyboard overlaps with self.view
// self.view contains self.textView
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame fromView:nil];
CGFloat textViewWidth = self.view.frame.size.width;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
self.textView.frame = CGRectMake(0, 0, textViewWidth, keyboardFrame.origin.y);
[UIView commitAnimations];
}
// When the keyboard hides return self.textView to fullscreen
-(void)keyboardWillHide:(NSNotification *)_notification {
NSDictionary *userInfo = [_notification userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
self.textView.frame = self.view.frame;
[UIView commitAnimations];
}
The snippet contains some extra stuff to animate the transition in sync with the keyboard animation.
In this snippet I use self.textView, which you will probably want to change to self.scrollView. What's great about this code is that it works in all orientations and on the iPad. Take a look at the code and hopefully it'll be self explanatory.
use this method to check orientation & change content Size of scrollView according to orientation
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
I use the following method to resize the view after keyboard show/hide:
- (void)moveViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up {
NSDictionary* userInfo = [aNotification userInfo];
// Get animation info from userInfo
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
// Animate up or down
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
CGRect newFrame = self.view.frame;
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
newFrame.size.height += (up? -1 : 1) * keyboardFrame.size.height;
self.view.frame = newFrame;
keyboardUp = up;
[UIView commitAnimations];
}
This works very good, until the screen orientation changes. What happens then is that the method resizes the view correctly, but after this method returns - something else resizes the view again to the maximum height of the screen.
Any ideas?
First you'll need to figure out what is resizing the view again. I would recommend putting a breakpoint into the above method so when it gets called that 2nd, unwanted time, you can look at the method call stack and see what is causing the 2nd call. Then you'll be able to stop it from happening, or change your code so that it doesn't cause a problem.