This question already has answers here:
How can I make a UITextField move up when the keyboard is present - on starting to edit?
(98 answers)
Closed 8 years ago.
I have a question concerning "Moving Content That Is Located Under the Keyboard".
I have a simple "sign in" view with 2 UITextFields (username and password) and a sign in button.
When the user taps on one of those text fields the keyboard appears and obscurs the sign in button and the password text field.
So I implemented the solution proposed by Apple and one other (see links below) :
https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
http://ios-blog.co.uk/tutorials/how-to-make-uitextfield-move-up-when-keyboard-is-present/
But the problem is that the keyboard moves first and it's only when the keyboard has finished to move that the 2 UITextFields and the sign in button move. Thus it's not the best solution for User Experience...
Do you know I can move the keyboard and the 2 UITextFields at the same time ? Like on the iOS Facebook login page.
Thanks !
Well, you can do this in 2 ways: the easy way, and the hard way.
Easy way: Instead of a ViewController, use a TableViewController and create custom cells that contain the text fields. The TableViewController will take care of moving the view up for you when the keyboard goes up. Then all you have to worry about is to dismiss the keyboard whenever you want the keyboard to go away.
The hard way: Use a ViewController.
In order for this to work you need to have 2 notifications, like this ones (place this inside your viewDidLoad method):
// Keyboard notifications so things can move up when the keyboard shows up
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
Then you need to create the 2 methods you're calling in the NotificationCenter, to actually perform the movement of the screen:
- (void) keyboardWillShow: (NSNotification*) aNotification
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
CGRect rect = [[self view] bounds];
rect.origin.y -= 150;
[[self view] setFrame: rect];
[UIView commitAnimations];
}
// Call this to make the keyboard move everything down
- (void) keyboardWillHide: (NSNotification*) aNotification
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
CGRect rect = [[self view] frame];
if (rect.origin.y == -150)
{
rect.origin.y += 150;
}
[[self view] setFrame: rect];
[UIView commitAnimations];
}
In this code, the value of 150 is an arbitrary number of points that the keyboard will go up/down. You can change it or do some fancy calculation to get a percentage of the screen regardless of the screen size.
Hope it helps!
In this case, you should intercept the UIKeyboardWillShowNotification notification and offset your content animately with an animation block.
[UIView animateWithDuration:0.5 animations:^{
// your own animation code
// ...
} completion:nil];`
The same is for the UIKeyboardWillHideNotification notification.
Of course, if you are scrolling the content with an already-animated method, you can just put that without the need of an animation block.
Just shift content when UIKeyboardWillShowNotification is received. Instead of UIKeyboardDidShowNotification.
UPDATE
Also if you need to add animation you could use userInfo from keyboard notification to fetch keyboard height, speed of emerging and animation curve.
So this code should react for UIKeyboardWillShowNotification:
[UIView animateWithDuration:[note.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue] delay:0.0f options:[note.userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue] animations:^{
// TODO: shift content here
} completion:nil];
Also you need something similar for UIKeyboardWillHideNotification. Difference would be just direction of shifting inside animations block.
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 am using following code to animate view's position according to keyboard movement. The code works fine under iOS 7, however it causes strange behaviour under iOS 8:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
- (void) keyboardWillShow:(NSNotification*) aNotification {
NSDictionary* keyboardInfo = [aNotification userInfo];
NSTimeInterval time = [keyboardInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [keyboardInfo[UIKeyboardAnimationCurveUserInfoKey] intValue];
CGRect keyboardFrameEnd = [[keyboardInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
double offset = keyboardFrameEnd.size.width > keyboardFrameEnd.size.height ? keyboardFrameEnd.size.height : keyboardFrameEnd.size.width;
[UIView beginAnimations:#"moveWindow" context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationCurve:curve];
[UIView setAnimationDuration:time];
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y + offset, self.frame.size.width, self.frame.size.height);
[UIView commitAnimations];
}
Unfortunately, the answers mentioned in Is there a change to the behaviour of UIKeyboardWillShowNotification in iOS 8? and iOS8: What's going on with moving views during keyboard transitions? are out of the question:
The source code base is very large and old and use neither storyboard nor Auto Layout features.
I don't want to use UIKeyboardDidShowNotification event because the keyboard is already visible and the animation looks terrible (however, the problem is gone when using this type of notification).
Oddly enough, I found out that the problem is gone when the feature is used in the very first screen of the application. Further tests showed that removing MKMapView from one of the previous screens solves this issue. I triple checked that MKMapView is used and disposed correctly. Every allocated instance is gone way before the above code is executed.
After hours of testing and debugging I noticed in a visual hierarchy debugger (How do I inspect the view hierarchy in iOS?) that there are constraints added under UILayoutContainerView and UINavigationTransitionView. These constraints are not present when the MKMapView control is removed from the previous screen. I tried playing with every possible combination of setTranslatesAutoresizingMaskIntoConstraints: setup and could not find any resolution yet. Is it some sort of bug in iOS 8 itself or is there another way to animate view along with the keyboard?
I'm not sure about the code itself but from the documentation about those animation methods...
Use of this method is discouraged in iOS 4.0 and later. You should use the block-based animation methods to specify your animations instead.
The correct way to animate you view is...
[UIView animateWithDuration:time
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y + offset, self.frame.size.width, self.frame.size.height);
}
completion:nil];
Edit
OK, it's not AutoLayout (I think) but I'll leave this here anyway as you need to change it and I'll wait for more information in the question.
I am developing a chat app which has UITableView and a UIView containing a UITextField and a UIButton in it. I am using the following code to move the UIView up when keyboard appears.
(void)keyboardWillShow:(NSNotification *)notification
{
NSDictionary* info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
[UIView animateWithDuration:0.2f animations:^{
CGRect frame = self.inputView.frame;
UIInterfaceOrientation orientation = self.interfaceOrientation;
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
frame.origin.y -= kbSize.width;
else
frame.origin.y -= kbSize.height;
self.inputView.frame = frame;
;
}];
}
This code is working fine until iOS 7, but in iOS 8 UIView is not displaying above the keyboard.
Can anyone please suggest what could be the possible issue, or is there anything that has changed in iOS 8?
Your code seems to be correct but i will prefer using UIKeyboardDidChangeFrameNotification or UIKeyboardWillChangeFrameNotification because these will tell you the change in keyboard frame when predictive text bar gets up or down when keyboard is in view.
In your ViewDidLoad add this
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardFrameDidChange:)
name:UIKeyboardDidChangeFrameNotification object:nil];
and then paste this method in your ViewController
-(void)keyboardFrameDidChange:(NSNotification*)notification{
NSDictionary* info = [notification userInfo];
CGRect kKeyBoardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
[yourView setFrame:CGRectMake(0, kKeyBoardFrame.origin.y-yourView.frame.size.height, 320, yourView.frame.size.height)];
}
This will handle all your keyboard cases like when its up or down or change in its frame with predictive text bar
and also remove observer when you are leaving your view
The accepted Answer is almost right. To match your view's animation to that of the keyboard you want to use the UIKeyboardWillChangeFrameNotification rather than the UIKeyboardDidChangeFrameNotification. That way the animations you kick off will precisely match that of the keyboard. Here's some code to do the entire thing. I use the animation of the keyboard to drive the animation of my autolayout constraint constants, but you can easily adapt this to animate an entire view frame. (Note, we have to use the old school style animations to hook into the UIKeyboardCurveInfoKey which provides an animation curve exactly matching the keyboard animation.
In viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardFrameDidChange:)
name:UIKeyboardWillChangeFrameNotification
object:nil];
In ViewController:
- (void)keyboardFrameDidChange:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
CGRect kKeyBoardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat height = kKeyBoardFrame.size.height;
[self.view removeConstraints:self.verticalButtonConstraints];
NSDictionary *metrics = #{#"height" : #(height)};
NSDictionary *views = #{#"nextButton" : self.nextButton};
self.verticalButtonConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V: [nextButton(52)]-(height)-|" options:0 metrics:metrics views:views];
[self.view addConstraints:self.verticalButtonConstraints];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.view layoutIfNeeded];
[UIView commitAnimations];
}
I just ran into this and made a discovery I thought I would share. In iOS 8 layout passes for the sub views of the main view will be done whenever the keyboard is about to appear or about to go away. These passes do not get done on iOS 7. So if you try to animate a sub view of the main view in keyBoardWillShow or keyboardWillChangeFrame the animation will get undone by the layout pass and your sub views that you tried to animate will move back to their original position. That is why keyboardDidChangeFrame works to animate the subviews and keyboardWillChangeFrame does not.
Something odd I noted as well is the timing of when these calls are made. It seems the first time that the keyboard appears after the app is launched, the call to keyboardDidChangeFrame happens too late to be able to animate with the keyboard so they slide up together, but on second and subsequent times the keyboard shows, the call to keyboardDidChangeFrame happens sooner and it seems you can actually animate the views along with the keyboard.
I must note that I am using C# and Xamarin as my development platform for iOS, so this may be different when using Swift or Obj-C.
You can use an accessoryView, which will attach itself to the top of the keyboard. Alternatively, if you want more power over customization, you can use notifications as explained by #pankaj_wadwha to fetch the frame information. Bonus: you can also get the animation information (such as speed) so your view moves alongside the keyboard perfectly.
[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.
Im curious about a feature in iOS. Please help me out here if you can.
Scenario: I am using a text box where name is entered. Its on lower half of the screen. Just below the text box is a label which displays the number of characters remaining(e.g.like in a twitter feed).
Problem: When i place the text box in upper half of the screen. both the text field and label are visible. But when I place them in lower half, the apple keyboard covers the label part.
Is there a way where I control the area covered in such a way that the label below is also visible?
I hope I have made myself clear enough.
Thanks.
Here i have used delegate method for UITextView Same way you can do for UITextField
-Here in this code when user starts entering values in textview it makes your view's hight lesser then its original with animation
-When user Ends Entering values, it will make your view's size original size.
-(void)textViewDidBeginEditing:(UITextView *)textView
{
CGRect frame = self.view.frame;
frame.origin.y = -100;
[self.view setFrame:frame];
[UIView commitAnimations];
}
-(void)textViewDidEndEditing:(UITextView *)textView
{
CGRect frame = self.view.frame;
frame.origin.y = 0;
[self.view setFrame:frame];
[UIView commitAnimations];
}
If you want to know about delegates this link helps you
Well in that case you have to move the textbox when the keyboard pops up.You can have the notification registered to know when the keyboard pops up and a scrollview to scroll the whole content up the screen can do the job for you
See this question,It explains well how to manage something like this
AFAIK You can't control the size of iOS native Keyboard, all you can and should be doing is, making them a subivew of a scroll view and scroll it up.
So the usual practice go something like this.
Subscribe to the Keyboard notification. UIKeyboardWillShowNotification
In the method which the Notification listener will be invoking, set the scrollView's content size accordingly and set the content offset.
self.scrollView.contentSize = CGSizeMake(320, 267);
self.scrollView.contentInset = UIEdgeInsetsMake(0, 0, localKeyboardFrame.size.height, 0);
[self.scrollView scrollRectToVisible:<rect of view you want to scroll to> animated:YES];
Undo the changes when the keyboard hides, with the help of appropriate notification.UIKeyboardWillHideNotification
self.scrollView.contentInset = UIEdgeInsetsZero
And here is iOS Human Interface Guide's explanation on it.
Add following to your viewDidLoad Method
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(showKeyboard) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideKeyboard) name:UIKeyboardWillHideNotification object:nil];
And after that-- Declare the following 2 methods in your .m file
-(void)showKeyboard {
[UIView beginAnimations:#"slide" context:nil];
[UIView setAnimationDuration:0.3];
self.view.transform = CGAffineTransformMakeTranslation(0, -100);
[UIView commitAnimations]; }
-(void)hideKeyboard {
[UIView beginAnimations:#"slide" context:nil];
[UIView setAnimationDuration:0.1];
self.view.transform = CGAffineTransformMakeTranslation(0, 0);
[UIView commitAnimations]; }