I have a view-based application, and in one of the subviews there is a UIScrollView. I have written handlers to adjust the size of the scroll view when the keyboard appears and disappears. I would like the keyboard to be dismissed when the user leaves the view, so I call [currentField resignFirstResponder] in viewWillDisappear. This dismisses the keyboard, but does not call the handler to resize the scroll view (when I call the same code in other places, it does). Any suggestions?
EDIT: These are the handlers that I use:
-(void) keyboardWasShown:(NSNotification*) notification
{
if(keyboardShown)
return;
NSDictionary* info=[notification userInfo];
NSValue* value=[info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize keyboardSize=[value CGRectValue].size;
CGRect viewFrame=[scrollView frame];
viewFrame.size.height-=keyboardSize.height;
scrollView.frame=viewFrame;
keyboardShown=YES;
}
-(void) keyboardWasHidden:(NSNotification*) notification
{
NSDictionary* info=[notification userInfo];
NSValue* value=[info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize keyboardSize=[value CGRectValue].size;
CGRect viewFrame=[scrollView frame];
viewFrame.size.height+=keyboardSize.height;
scrollView.frame=viewFrame;
keyboardShown=NO;
}
When I call [currentField resignFirstResponder] anywhere else, it calls the handler without problems.
So you were being removed as observer before UIKeyboardDidHideNotification was posted, glad I could help. But observing the UIKeyboardWillHideNotification and UIKeyboardWillShowNotification is probably enough for your reaction to the keyboard. The keyboard notifications have a user info key UIKeyboardAnimationDurationUserInfoKey which you can use to animate your frame adjustments with the keyboard animations. This avoids the 'clunk' feeling your views will have if you don't animate them to new positions. Here is a quick example of what you can do:
-(void)keyboardWillNotificationTarget:(NSNotification *)note{
// Find current keyboard origin Y
NSValue *keyboardCurrentFrameValue = [note.userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey];
CGFloat currentOriginY = keyboardCurrentFrameValue.CGRectValue.origin.y;
// Find keyboard Y that will be
NSValue *keyboardNewFrameValue = [note.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGFloat newOriginY = keyboardNewFrameValue.CGRectValue.origin.y;
// Calculate new frame for scrollView
CGFloat heightChangeForScrollView = newOriginY - currentOriginY;
CGRect svFrame = scrollView.frame;
svFrame.size.height += heightChangeForScrollView;
// Find duration of animation
NSNumber *animationDurationNumber = [note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
CGFloat animationDuration = animationDurationNumber.floatValue;
// Animate scrollView with keyboard
[UIView animateWithDuration:animationDuration animations:^{
scrollView.frame = svFrame;
}];
}
Now you simply add this method as the target for both notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillNotificationTarget:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillNotificationTarget:) name:UIKeyboardWillHideNotification object:nil];
Related
I am working on a chat application similar to whatsapp etc. It has a tableview in view controller and a text field and button in the bottom toolbar. I came across the various question on sliding the view upwards and using this link I managed to slide the view upwards. However I want to dismiss the keyboard and the view comes down and fits the screen .I tried using tap gesture and click on return button but nothing seems to work. How do I do make the view slide down and keyboard disappear?
Moreover how can i change the width of text field so that multiple lines can appear when the user is writing the message?
you can add tap gesture event to tableview cell and also you can use touch event method when user click on tableview then according to keyboard previous state you can display or hide keyboard. Hope this will help to u.
Use textFieldShouldReturn to resign first responder status (dismiss the keyboard) and slide the view up.
Personally I do it this way:
I register for notifications to know when the keyboard will be shown, and when it will be hidden.
When the keyboard appears, I set the view insets to include the size of the keyboard.
Slide the view up
When the keyboard will disappear, I set the insets to zero.
TextField Delegate Method to hide the keyboard when the Return button is tapped
-(BOOL)textFieldShouldReturn:(UITextField*)textField;
{
[textField resignFirstResponder];
return NO; // We do not want the UITextField to insert line-breaks.
}
Register for keyboard appear/disappear notifications
- (void)viewDidLoad
{
...
// Register for notifications for when the keyboard will appear and disappear
[self registerForKeyboardNotifications];
}
// 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.
// Original code for this part here: http://stackoverflow.com/a/16044603/4518324
- (void)keyboardWasShown:(NSNotification *)note
{
NSDictionary *userInfo = note.userInfo;
NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
CGRect keyboardFrameEnd = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardFrameEnd = [self.view convertRect:keyboardFrameEnd fromView:nil];
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | curve animations:^{
self.view.frame = CGRectMake(0, 0, keyboardFrameEnd.size.width, keyboardFrameEnd.origin.y);
} completion:nil];
}
- (void)keyboardWillBeHidden:(NSNotification *)note
{
NSDictionary *userInfo = note.userInfo;
NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
CGRect keyboardFrameEnd = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardFrameEnd = [self.view convertRect:keyboardFrameEnd fromView:nil];
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | curve animations:^{
self.view.frame = CGRectMake(0, 0, keyboardFrameEnd.size.width, keyboardFrameEnd.origin.y);
} completion:nil];
}
I have created sample code that involves resizing the view when they keyboard is shown or dismissed.
https://github.com/gingofthesouth/KeyboardHideShow
I got it right . I had another method called out when keyboard is dismissed which fit the view frame as per requirement which is View.frame-keyboard.frame.height. Thanks anyways!:)
I have some custom views with embedded table views in them. So I had to program the whole scrolling up etc when the keyboard appears myself. I've used the apple docs for that and came up with this:
- (void)viewDidLoad
{
[super viewDidLoad];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification object:nil];
[center addObserver:self selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)keyboardDidShow:(NSNotification *)notification
{
// keyboard frame is in window coordinates
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// convert own frame to window coordinates, frame is in superview's coordinates
CGRect ownFrame = [self.tableView.window convertRect:self.tableView.frame fromView:self.tableView.superview];
// calculate the area of own frame that is covered by keyboard
CGRect coveredFrame = CGRectIntersection(ownFrame, keyboardFrame);
// now this might be rotated, so convert it back
coveredFrame = [self.tableView.window convertRect:coveredFrame toView:self.tableView.superview];
// set inset to make up for covered array at bottom
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, coveredFrame.size.height, 0);
self.tableView.scrollIndicatorInsets = self.tableView.contentInset;
}
- (void)keyboardWillHide:(NSNotification *)notification
{
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
self.tableView.scrollIndicatorInsets = self.tableView.contentInset;
}
But now the not so nice thing about this (and another code I've tried, same results) is: When scrolling around the table and switching back to non-edit mode, some cells which where hidden before at the bottom of the table are immediately in the non-edit mode and not transitioning back from the indented state. Very rarely this results even in an error, that one cell remains in edit mode even though all cells have been switched back. This error disappears if I scroll the cell away and back and then it's in the correct format.
Any idea why? I've definitely narrowed it down to this code. When I comment everything out it works as it should (well of course without the scrolling when the keyboard appears :-)
[UIView animateWithDuration:0.6 delay:0.0 options:UIViewAnimationCurveLinear animations:^{
[self.searchField becomeFirstResponder];
} completion:nil];
animates the keyboard entry by having it appear as if its far away and in the top left corner of the screen, swinging inward in a slightly curved line to it's final resting spot.
This happens no matter what UIView animation method I use.
Even stranger, it only happens once. Not just once per view controller, but ONCE, period. Dispatch_once style.
The effect I'm looking for (which works all subsequent times my method is called, and works for [self.view endEditing]) is just a standard linear entry every time. I need this inside an animation block to keep it on the same time schedule as an animation I have that slides down my searchField from under the status bar.
My question - How can I guarantee consistent animation of [viewObject becomeFirstResponder] inside UIView animation blocks?
The reason the keyboard comes from the top left corner the first time you animate it is because it's initial position and size is (0,0,0,0). So on first creation (the first animation block) it animates from (0,0,0,0) to whatever the frame is when it is visible on screen. Subsequent calls to that animation will show the keyboard in the right initial position and then animate properly.
In my research I found someone say it is very bad practice to put Apple built in animations inside your animation blocks, and I agree.
The way to get around this is to listen for keyboard motion, get the properties from the keyboard Apple defined animation and do your animating with the same properties. Something like this from this answer:
Setup your keyboard listening:
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)dealloc {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
And then setup the functions to mimick they keyboard animation properties:
- (void)keyboardWillHide:(NSNotification *)n
{
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// resize the scrollview
CGRect viewFrame = self.scrollView.frame;
// I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
viewFrame.size.height += (keyboardSize.height - kTabBarHeight);
NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval animationDuration;
[animationDurationValue getValue:&animationDuration];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:animationDuration];
//Your animations go here
[UIView commitAnimations];
keyboardIsShown = NO;
}
You should be able to extract what you need from these bits of code and make your app look exactly like you would like it to. Good luck.
You can use the values for UIKeyboardAnimationCurveUserInfoKey and UIKeyboardAnimationDurationUserInfoKey keys of userInfo property from UIKeyboardWillShowNotification to synchronize your animation with the keyboard animation.
I've just found an annoying bug with the new keyboardDismissMode property of the scroll view. When using this with a text view with the value UIScrollViewKeyboardDismissModeInteractive and the keyboard is dismissed the scroll view seems to jump up to the top before it continues to decelerate.
I've filed a bug report with Apple but need a workaround. I've tried the DAKeyboardControl without the new iOS7 support which behind the scenes is using the keyboardDismissMode and it still does it which to me indicates this is a much deeper problem.
Any suggestions?
for this issue better you code with scrollviewDelegete and simply mention when you want dismiss keyboard through ResignFirstResponder
Does seem to be a bug or just a non-ideal default state. But based on the code in the test project something like the below may work after some finer tuning.
There are two problems with the sample code, one is that you aren't doing anything about the size of the text when the keyboard does appear, so you can't use or see the text under the keyboard. There are other solutions but a quick and dirty solution is to change the frame size (in a submission app I would also grab the animation info and animate the view frame change to match the keyboard animation which is beyond the scope of this question). You do that in 'willShow' or the like, and bring it back in 'didHide' or the like.
Then, the content offset is fudged when its hidden and there does appear to be some strange states while you are dragging it offscreen before and around your callbacks for hiding and scroll view changes. I just save the state and "fix" it once the keyboard goes away and I've updated the text view.
I created a few properties and an outlet in the storyboard to fudge with the text view.
- (void) viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
}
- (void) keyboardWillShow:(NSNotification *)notification
{
NSDictionary * info = [notification userInfo];
CGSize size = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect rect = self.textView.frame;
rect.size.height -= size.height;
self.textView.frame = rect;
}
- (void)keyboardDidHide:(NSNotification *)notification
{
NSLog(#"====== keyboardDidHide =======");
NSDictionary * info = [notification userInfo];
CGSize size = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect rect = self.textView.frame;
rect.size.height += size.height;
self.textView.frame = rect;
self.hidingKeyboard = YES;
}
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(#"%f", scrollView.contentOffset.y);
if(self.hidingKeyboard == YES)
{
scrollView.contentOffset = self.lastOffset;
self.hidingKeyboard = NO;
NSLog(#"====== reset =======");
}
else
self.lastOffset = scrollView.contentOffset;
}
I have two UIViews and UIView2 got UITextView which can be placed wherever user likes to add Text label. And when user places UiTextView in the bottom, keyboard appears to type text and UITextView moves up. And it works great! I need also move UIView1 which is under the UIView1.
UIView2 is property of UIView1 and i need Notify UIView1 to do Move Up methods for it when UITextView of UIView2 becomeFirstResponder.
[[NSNotificationCenter defaultCenter] addObserver:self.drawingView
selector:#selector(keyboardWillShow)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self.drawingView
selector:#selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
You've add notification for keyboard Events then what you have to do is to implement the method keyboardWillShow and keyboardWillHide. See below
- (void)keyboardWillShow:(NSNotification *)notification {
/*
Reduce the size of the text view so that it's not obscured by the keyboard.
Animate the resize so that it's in sync with the appearance of the keyboard.
*/
NSDictionary *userInfo = [notification userInfo];
// Get the origin of the keyboard when it's displayed.
NSValue* aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
// Get the top of the keyboard as the y coordinate of its origin in self's view's coordinate system. The bottom of the text view's frame should align with the top of the keyboard's final position.
CGRect keyboardRect = [aValue CGRectValue];
// Get the duration of the animation.
NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval animationDuration;
[animationDurationValue getValue:&animationDuration];
// Animate the resize of the text view's frame in sync with the keyboard's appearance.
[self moveCommentBarViewWithKeyBoardHeight:keyboardRect.size.height withDuration:animationDuration];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary* userInfo = [notification userInfo];
/*
Restore the size of the text view (fill self's view).
Animate the resize so that it's in sync with the disappearance of the keyboard.
*/
NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval animationDuration;
[animationDurationValue getValue:&animationDuration];
[self moveCommentBarViewWithKeyBoardHeight:0 withDuration:animationDuration];
}
hope it could hlep:)
PS: when add this notification, you better add to method -(void)viewWillAppear:(BOOL)animite. And use
[[NSNotificationCenter defaultCenter] removeObserver:self];
to removew observe when viewWillDisappear.
-(void)moveCommentBarViewWithKeyBoardHeight:(CGFloat)kHeighy withDuration:(NSTimeInterval)animationD
{
CGRect tempRect = commentEditedBarView.frame;
[UIView beginAnimations:#"Animation" context:nil];
[UIView setAnimationDuration:animationD];
[commentEditedBarView setFrame:CGRectMake(tempRect.origin.x, self.view.frame.size.height-kHeighy-tempRect.size.height, tempRect.size.width, tempRect.size.height)];
[UIView commitAnimations];
}