I have a couple UITextFields that will be hidden by the keyboard when it pops up, so I implemented the UIKeyboardWillShowNotification and UIKeyboardWillHideNotification and sent them to their respective methods:
- (void)keyboardWillShow:(NSNotification*)notification
{
CGFloat height = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
keyBoardHeight = height;
}
- (void)keyboardWillHide:(NSNotification*)notification
{
CGFloat height = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
keyBoardHeight = height;
}
I then have my textfields delegating to this method:
//TextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
float keyboardOffset = [self offsetForKeyboard:textField.frame.origin.y withHeight:textField.frame.size.height];
NSLog(#"%f, %f", keyBoardHeight, keyboardOffset);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.1];
self.view.frame = [Global rect:self.view.frame withNewY:-[self offsetForKeyboard:textField.frame.origin.y withHeight:textField.frame.size.height]];
[UIView commitAnimations];
}
The code runs properly, but when I run the code, the keyboardWillShow: method runs after the textFieldDidBeginEditing: method. Because of this, keyboardHeight is set to 0 the first time the code runs, and so the offset I calculate is way off.
How can I make the keyboardWillShow: method run before the textFieldDidBeginEditing: method? Is there another delegate method that I can use instead?
Sorry, but no there's no way to do that. You have to do your animations or whatever you do for keyboard handling inside the notification observer.
Here's what Apple recommends and works fine for me in all cases I've encountered upto now.
Apple's Keyboard Guide
For anyone else who is trying to do this, I found a way:
Maintain a weak reference to the currently editing text field:
UITextField* editingTextField;
- (void)textFieldDidBeginEditing:(UITextField*)textField
{
editingTextField = textField;
}
- (void)textFieldDidEndEditing:(UITextField*)textField
{
editingTextField = nil;
}
Then just reference the weak text field while it is open in the keyboardWillShow: method to calculate the appropriate offsets and move the view accordingly.
- (void)keyboardWillShow:(NSNotification*)notification
{
//Use editingTextField's frame here
}
Simply use IQKeyboardManager. It is a single line code for all textfield in any view.Link: https://github.com/hackiftekhar/IQKeyboardManager
Related
I have a UITextView that animates its frame when the keyboard appears like so:
- (void)keyboardWasShown:(NSNotification*)n {
double duration = [[[n userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:duration
delay:0.3
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect tfFrame = self.tf.frame;
tfFrame.origin.y = tfFrame.origin.y - kbSize.height + 86.f;
[self.tf setFrame:tfFrame];
}
completion:nil
];
}
There is another UITextField directly below this text field which also animates into place when the keyboard appears. After animation, when the keyboard appears, and I touch the second text field, all of the animations revert back into their original positions.
I've logged the frame's y value through each of the UITextField delegate methods to try to pinpoint where the field is changing, and it seems to change when the first field resigns first responder:
-(void)textFieldDidBeginEditing:(UITextField *)textField {
NSLog(#"did begin: %f", self.tf.frame.origin.y);
}
-(void)textFieldDidEndEditing:(UITextField *)textField {
NSLog(#"did end: %f", self.tf.frame.origin.y);
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
NSLog(#"should end: %f", self.tf.frame.origin.y);
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSLog(#"should return: %f", self.tf.frame.origin.y);
return YES;
}
From the console:
2015-03-27 did begin: 521.000000
2015-03-27 should end: 90.294118
2015-03-27 did end: 521.000000
So far I have:
Tried to modify the autolayout constrains when I adjust the frame
Tried to completely remove the autolayout constraints
Unfortunately, I don't know where to go from here. I believe this to be an issue with either first responder status on the text fields or autolayout. What should I start checking?
In my app I've this login screen:
Above the first test field I've an image, the configuration in Interface Builder is the follow:
Now when I tap on the UITextField they should move up otherwise in iPhone 4/4s the field will be covered by keyboard.
Now I'm using the following code:
-(void)textFieldDidBeginEditing:(UITextField *)textField {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frameView = self.view.frame;
CGRect frameImage = self.imageViewLogo.frame;
CGRect frameTextFieldUsername = self.textFieldUsername.frame;
CGRect frameTextFieldPassword = self.textFieldPassword.frame;
CGRect frameButtonLogin = self.buttonLogin.frame;
frameView.origin.y = -100;
frameImage.origin.y = -100;
frameTextFieldUsername.origin.y = -100;
frameTextFieldPassword.origin.y = -100;
frameButtonLogin.origin.y = -100;
[self.view setFrame:frameView];
[self.imageViewLogo setFrame:frameImage];
[self.textFieldUsername setFrame:frameTextFieldUsername];
[self.textFieldPassword setFrame:frameTextFieldPassword];
[self.buttonLogin setFrame:frameButtonLogin];
[UIView commitAnimations];
}
When I try to run the app in simulator or on a real device the view scrolls up of 100 but the image, the text fields and button doesn't scrolls up... I thought that the problem depend on the constraints, can you help me to fix this issue?
Thank you
The way in which you are trying to do this will get you in trouble. As you are giving hardcoded values of frame.
Try to use keyboard avoiding library it's the better and safer way of doing it.
-(void)textFieldDidBeginEditing:(UITextField *)textField {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frameView = self.view.frame;
CGRect frameImage = self.imageViewLogo.frame;
CGRect frameTextFieldUsername = self.textFieldUsername.frame;
CGRect frameTextFieldPassword = self.textFieldPassword.frame;
CGRect frameButtonLogin = self.buttonLogin.frame;
frameView.origin.y = -100;
// no need of changing frames of TextFields inside the View, Just change the y-padding of View
[self.view setFrame:frameView];
[UIView commitAnimations];
}
reduce the y-padding to -120 and check
Hope this helps thanks
If your views are set up to use AutoLayout, changing frame will do nothing.
You should subscribe to UIKeyboardWillChangeFrameNotification and perform your changes there. In case of constraints you would update the constrains constants here and the call -setNeedsUpdateConstraints on the superview.
You have to figure out if the keyboards is appearing or disappearing at all if you want to support external keyboard.
Otherwise you can listen for UIKeyboardWillShowNotification and UIKeyboardWillHideNotification
For auto up the textFields while keyboard comes not need to use the thirdParty Classes.
Create a static tableView and design each textFields and button in each static cells.
TableView automatically handle the auto up textFields while keyboard comes.
Hi the simple and easy trick for doing it is :-
Take outlet of top constraint of username or email id textfield in your class and on textfield did begin editing delegate decrease the constraint's constant value so it will goes up when u select the textfield and at textField did end editing delegate again set constraint's constant value to previous one so it will reset on previous position.
eg:-Assuming you set Top Constraint value in Storyboard is 150
-(void)textFieldDidBeginEditing:(UITextField *)textField {
_topConstraint.constant = 10.00;
[UIView animateWithDuration:0.5f animations:^{
[self.view layoutIfNeeded];
}];
}
-(void)textFieldDidEndEditing:(UITextField *)textField {
_topConstraint.constant = 150.00;
[UIView animateWithDuration:0.5f animations:^{
[self.view layoutIfNeeded];
}];
}
Hope it will help you.
I am experiencing a strange error on iOS 8 when trying to synchronize an animation in tandem with the keyboard moving up after selecting a textfield.
Specifically, I am trying to get the height of the keyboard in order to know how far up to move the text field by using the following code after registering for the keyboardwillshow notification:
- (void)keyboardWillShow:(NSNotification *)notification
{
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
self.keyboardHeight = keyboardSize.height;
[self moveToolbarUp];
}
-(void)moveToolbarUp
{
self.keyboardExpanded = YES;
CGRect frame = self.toolbar.frame;
DLog(#"keyboard height now:%f",self.keyboardHeight);
frame.origin.y = (self.view.frame.size.height-toolbarHeight-self.keyboardHeight);
[UIView animateWithDuration:0.1 animations:^{
self.toolbar.frame = frame;
}];
}
However, if I try to set a break point at the keyboardwillshow method, I can see that the keyboard is already all the way up when the method is called, and the animation is therefore delayed (the keyboard pops up and then a second or so later, the textfield follows).
Has anybody else experienced this error? I am running on an iPhone 5s, iOS 8.0.2.
Thanks!!
It's not an error. The fact is that you can remove your animateWithDuration call. In iOS 8, keyboardWillShow: is called inside the keyboard's animation. You don't have to synchronize anything; anything you do will be part of the animation, automatically.
Im dynamically creating textFields on UIScrollView, I'm animating View to bring textField above the keyboard if keyboard is hiding it. To animate View is used code given below:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGRect textFiledFrame = textField.frame;
if (textFiledFrame.origin.y > 219 && textField.tag > 150 && viewAnimated == 0) {
CGRect superViewFrame = textField.superview.frame;
superViewFrame.origin.y = superViewFrame.origin.y - 120;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.6];
[textField.superview setFrame:superViewFrame];
[UIView commitAnimations];
viewAnimated = 1;
}
}
for every single time i'm clicking on textFields textFieldDidBeginEditing:(UITextField *)textField is being call but once view get animated, textFieldDidBeginEditing:(UITextField *)textField is not being called
There are several things wrong with your code:
You should not hardcode any keyboard related sizes. Instead make use of the keyboard information the iOS framework provides you (see e.g. here: How to make a UITextField move up when keyboard is present?)
You do set ViewAnimated = 1 in your if statement - do you ever rest it to 0? If not, the if statement will never be entered again, as you check on ViewAnimated == 0 in the if clause ...
Variables should not start with a capital letter (Rect, ViewAnimated) - better is textRect en viewAnimated
I have a UITableView inside a UIScrollView. The tableView does not span the length of the view as I have a UITextView at the bottom of the view (to enter messages) so the table ends at the textview. However, The scrollView does span the entire length of the view. When the keyboard comes up, I move the entire view up with it,(so nothing is hidden) but that makes the top cells of the table inaccessible (as in, I don't need to see the cells, but when i try to scroll to them I can't get to them) as it is pushing the whole view up. I am wondering if instead, I can push the scrollview underneath up, in turn having it push the keyboard up with it, and then have the tableview stay in place and scroll to the proper cell (for me in this case it is the most recent message, or last cell in the table). Having the tableview stay in place will allow scrolling access to all the cells in the table. Am I going about this the right way or is there an easier and better way to accomplish this? I'm working on a messaging app, and I want the UI to behave in a similar way to the native messages app. When the keyboard comes up, it brings the last message up above the top of the keyboard, but when the keyboard is visible you can still scroll to the top of the table. I will post my code for the movement of the view.
//call move view method and scroll to last cell in table
-(void) keyboardWasShown:(NSNotification*)aNotification
{
NSLog(#"Keyboard was shown");
[self moveView:[aNotification userInfo] up:YES];
[self.chatTable scrollRectToVisible:CGRectMake(0, self.chatTable.contentSize.height - self.chatTable.bounds.size.height, self.chatTable.bounds.size.width, self.chatTable.bounds.size.height) animated:YES];
}
-(void) keyboardWillHide:(NSNotification*)aNotification
{
NSLog(#"Keyboard will hide");
[self moveView:[aNotification userInfo] up:NO];
}
- (void)moveView:(NSDictionary*)userInfo up:(BOOL)up
{
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]getValue:&keyboardEndFrame];
UIViewAnimationCurve animationCurve;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey]
getValue:&animationCurve];
NSTimeInterval animationDuration;
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]
getValue:&animationDuration];
// Get the correct keyboard size to we slide the right amount.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
int y = keyboardFrame.size.height * (up ? -1 : 1);
self.view.frame = CGRectOffset(self.view.frame, 0, y);
[UIView commitAnimations];
}
What I would do is NOT move the whole view up, but just shrink the scrollView's height by y amount.
So instead of
self.view.frame = CGRectOffset(self.view.frame, 0, y);
you could
CGRect scrlRect = self.myScrollView.frame;
self.myScrollView.frame = CGMakeRect(scrlRect.origin.x, scrlRect.origin.y, scrlRect.size.width, scrlRect.size.height+y);
The first step in dealing with this is modify the insets on the table. This gist would help you ensure that the tableView (or scrollview) scrollable area is no longer covered by the keyboard: https://gist.github.com/TimMedcalf/9505416
It may reveal further issues, but that's always my first port of call.
In your keyboarWasShown method wright below code:
- (void)keyboardWillShow:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
float kbHeight = kbSize.width > kbSize.height ? kbSize.height : kbSize.width;
self.keyboardHeight.constant = kbHeight+5;
}
Now in your textFieldShouldBeginEditing delegate method of UITextField write below code:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
CGPoint point = [textField.superview convertPoint:textField.frame.origin toView:yourTableViewHere];
NSIndexPath *indexPath = [tblAsset indexPathForRowAtPoint:point];
CGRect tableViewVisibleRect = [tblAsset rectForRowAtIndexPath:indexPath];
if (point.y > (tableViewVisibleRect.size.height - self.keyboardHeight.constant))
{
CGPoint contentOffsetPoint = tblAsset.contentOffset;
contentOffsetPoint.y -= self.keyboardHeight.constant;
CGSize contectSize = tblAsset.contentSize;
contectSize.height += self.keyboardHeight.constant;
[tblAsset setContentSize:contectSize];
[tblAsset setContentOffset:contentOffsetPoint animated:NO];
}
}
Above is from my current project and its working somewhat like what you want. Using this way may help you achive your task.