Related
I am working on a app which contains UITextField and UITextView in static UITableView. I am facing two issues
When I select UITextField it moved correctly but it is not working for UITextView.
When the keyboard disappears the UITableView not shown properly.
All I want is to adjust UITextField and UITextView accordingly when keyboard appears and disappears. Here is my code.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
- (void)keyboardWillShow:(NSNotification *)notification {
//get the end position keyboard frame
NSDictionary *keyInfo = [notification userInfo];
CGRect keyboardFrame = [[keyInfo objectForKey:#"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
//convert it to the same view coords as the tableView it might be occluding
keyboardFrame = [self.tableView convertRect:keyboardFrame fromView:nil];
//calculate if the rects intersect
CGRect intersect = CGRectIntersection(keyboardFrame, self.tableView.bounds);
if (!CGRectIsNull(intersect)) {
//yes they do - adjust the insets on tableview to handle it
//first get the duration of the keyboard appearance animation
NSTimeInterval duration = [[keyInfo objectForKey:#"UIKeyboardAnimationDurationUserInfoKey"] doubleValue];
//change the table insets to match - animated to the same duration of the keyboard appearance
[UIView animateWithDuration:duration animations:^{
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, intersect.size.height, 0);
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, intersect.size.height, 0);
}];
}
}
- (void) keyboardWillHide: (NSNotification *) notification{
NSDictionary *keyInfo = [notification userInfo];
NSTimeInterval duration = [[keyInfo objectForKey:#"UIKeyboardAnimationDurationUserInfoKey"] doubleValue];
//clear the table insets - animated to the same duration of the keyboard disappearance
[UIView animateWithDuration:duration animations:^{
self.tableView.contentInset = UIEdgeInsetsZero;
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}];
}
Try with the UITableView's instance method: scrollToRowAtIndexPath:atScrollPosition:animated:
In keyBoardWillShow use the tableview's or the textfield's index path and set the scroll position to UITableViewScrollPositionTop.
If it doesn't work as you want it to, try with scrollToRowAtIndexPath:atScrollPosition:animated:
Hope this helps, good luck! :))
For further information, check out Apple's UITableView reference: https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html
You can use a third party library for avoiding keyboard scrolling above of text field or text views.
Use https://github.com/cokecoffe/ios-demo/blob/master/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m
OR
You can use UITableviewController as your view controller class.Change the Tableview content property to Static. Then you can add any UI controls to the static cells of the table view from story board.
If you select any text field or text view in your tableview, then key board automatically shows below of your text field or text view without any programming logic.
I have a form with a few text fields on a scroll view. I was trying to solve the problem of the keyboard hiding some text fields, which I partly did. At least it works well when I tap on each individual field. I used the recommended Apple approach:
I have registered for keyboard notifications in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:self.view.window];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:self.view.window];
I am tracking active text fields:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
activeTextField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
activeTextField = nil;
}
I am scrolling the view up when the keyboard shows up:
- (void)keyboardWillShow:(NSNotification *)aNotification {
// Get the size of the keyboard
NSDictionary* info = [aNotification userInfo];
keyboardHeight = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
CGRect aRect = self.view.frame;
aRect.size.height -= keyboardHeight + 44 + 44; // Compensates for Navbar and text field height
if (!CGRectContainsPoint(aRect, activeTextField.frame.origin) ) {
[scrollView scrollRectToVisible:activeTextField.frame animated:YES];
}
}
I then scroll the view back to default when the keyboard is hidden (I won't paste the code simply because it works fine).
However, out of my 5 text fields, the first 4 have a Next button on the keyboard (instead or Return) while the last field has Done. The idea is that I want the user to jump from one text field to another (in one direction is enough in my case). So, I've implemented a UITextField delegate method to handle that as well:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == firstNameTextField) {
[lastNameTextField becomeFirstResponder];
} else if (textField == lastNameTextField) {
[countryTextField becomeFirstResponder];
} else if (textField == cityTextField) {
[zipCodeTextField becomeFirstResponder];
} else if (textField == zipCodeTextField) {
[zipCodeTextField resignFirstResponder];
}
return NO;
}
The middle text field above is skipped, because for that text field I'm using a different input type (a custom view with a UIPickerView and a bar on top with Next button) - the missing code is in this method:
- (IBAction)goToNextTextField:(id)sender {
[cityTextField becomeFirstResponder];
}
OK, as I've mentioned, view adjustment works well when tapping individual text fields (and then dismissing the keyboard), even though keyboard sizes (standard iOS vs my custom view) are different heights. I can also successfully go through all text fields tapping on Next buttons.
Here are my issues though:
When tapping on Next, if the keyboard is not changing (say, from field 4 to 5 that both use standard keyboard), my keyboardWillShow: method is not called, NSLog debugger shows keyboardHeight as 0, and yet the view moves up unpredictably.
Also, when navigating to and from field 3 (the one that uses custom input view), keyboardHeight is thus not recalculated. I have tried registering to UIKeyboardDidChangeFrameNotification and UIKeyboardWillChangeFrameNotification (pointing to keyboardWillShow: method), but without much success. It is worth noting that I do see in console that keyboardHeight is changing, but it's usually lagging one step, i.e. keyboardHeight is updated when I leave the field, not when it becomeFirstResponder.
Perhaps a pair of experienced eyes will spot my mistake, for I have been destroying my pair of eyes searching for the solution for the last 2 days..
Thanks!
You can use UITextField delegates . Whenever user start editing in any textfield its delegate is called you can change scrollview offset using
- (void)scrollViewToCenterOfScreen:(UIView *)theView
{
CGFloat viewCenterY = theView.center.y;
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
CGFloat availableHeight = applicationFrame.size.height - 200; // Remove area covered by keyboard
CGFloat y = viewCenterY - availableHeight / 2.0;
if (y < 0)
{
y = 0;
}
[scrollView setContentOffset:CGPointMake(0, y+20) animated:YES];
}
So in TextField delegate you can set
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if([textField isEqual:textfield1])
{
[self scrollViewToCenterOfScreen:textfield1];
}
else if([textField isEqual:textfield2])
{
[self scrollViewToCenterOfScreen:textfield2];
}
return YES;
}
and when user press done or return button you can change offset to (0,0)
[scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
Hope this works for you.
https://github.com/simonbs/BSKeyboardControls
this control can show a toolbar above the keyboard when editing a textfield like this
==========================================
-(void)textFieldDidBeginEditing:(UITextField *)textField {
if (textField == firstNameTextField) {
//move to firstNameTextField
} else if (textField == lastNameTextField) {
//move to lastNameTextField
} else if (textField == cityTextField) {
//move to cityTextField
} else if (textField == zipCodeTextField) {
//move to zipCodeTextField
}
return NO;
}
I have in the end implemented a solution that works well for me. It's a mixture of Apple recommended approach, a couple of solutions found on these forums as well as my own.
First of all, register as UITextFieldDelegate on view controller .h file.
Then in viewDidLoad register for keyboard notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:self.view.window];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:self.view.window];
Don't forget to unregister from them (removeObserver:) in viewDidUnload method as well.
Let your application know which text field is currently active:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
activeTextField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
activeTextField = nil;
}
When your application receives a notification that keyboard will show, it calls this method:
- (void)keyboardWillShow:(NSNotification *)aNotification {
keyboardHeight = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
[scrollView setFrame:CGRectMake(scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
scrollView.frame.size.height - keyboardHeight)];
[self moveViewWithKeyboard];
}
All I'm doing here is decreasing the size of my scrollView frame by the size of the keyboard. After that I call my moveViewWithKeyboard: method that will make active view visible.
Interestingly enough, to solve the problem of correct keyboard height being detected one step too late (read my original question above), I had to change the argument UIKeyboardFrameBeginUserInfoKey in keyboard height detection line with UIKeyboardFrameEndUserInfoKey argument. Having done so, keyboard (or custom view) is detected when moving to the relevant field, not when moving away from it.
Of course, I have to restore original view frame when the keyboard hides (and note that I'm still calling the moveViewWithKeyboard: method with nil as an argument):
- (void)keyboardWillHide:(NSNotification *)aNotification {
[scrollView setFrame:CGRectMake(scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
scrollView.frame.size.height + keyboardHeight)];
[self moveViewWithKeyboard];
}
I also added calls to move view when keyboard hide/show is not triggered. This happens when I jump from text field to text field that have system keyboard as input, so it is neither hidden nor shown again (and no notifications are issued to trigger delegate methods).
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == firstNameTextField) {
[lastNameTextField becomeFirstResponder];
} else if (textField == lastNameTextField) {
[countryTextField becomeFirstResponder];
} else if (textField == cityTextField) {
[zipCodeTextField becomeFirstResponder];
} else if (textField == zipCodeTextField) {
[zipCodeTextField resignFirstResponder];
}
[self moveViewWithKeyboard:nil];
return NO;
}
And finally my method to move view:
- (void)moveViewWithKeyboard {
if (activeTextField) {
[scrollView scrollRectToVisible:activeTextField.frame animated:YES];
} else {
[scrollView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
}
}
Note that when no text field is passed as an argument (i.e. no text field is active), view is scrolled to its original position.
Hope this helps someone.
Im working on getting my UIscroll to scroll when ever a textfield is blocked by the keyboard by following this documentation
http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html#//apple_ref/doc/uid/TP40009542-CH5-SW7
However sadly...there is a variable mentioned, activeField and i cannot figure out how it is declared. I would really like if some probably advise how/where it is declared or even a solution to scrolling when the keyboard is activated will help.
Thank you
To answer your specific question, since the Apple Documentation is only ever using activeField for size data, you can simply declare it as a private global UIView *activeField and it will work for textFields, textViews, etc all the same.
However, their code actually doesn't work very well at all. I had to make some changes to their code to get mine to work correctly. This code is basically theirs with some minor tweaks to handle all of the cases listed below:
1) If you have a smaller scrollView nested inside of a view, not full screen scrollView and it still works.
2) If you want to move text fields down to the keyboard focus area as well as move them up from behind the keyboard.
3) Works on textViews and textFields of various sizes
4) If the keyboard is currently showing, it will move any newly tapped fields into the focus area
5) Works if you have your content already scrolled and invoke the keyboard
6) Works on all keyboard sizes for all devices (no hardcoded constants)
First off, create private variables for these:
UIView *_activeField;
CGFloat _keyboardHeight;
BOOL _isShowingKeyboard;
Next, just cut and paste this code into your ViewController (it looks like a lot but it's not that bad).
#pragma mark TextFieldKeyboardScrolling
- (void)registerForKeyboardNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)adjustInputFieldsForKeyboard {
CGFloat keyBoardTopInScrollView = self.view.frame.size.height - _keyboardHeight - self.scrollView.frame.origin.y;
CGFloat inputFieldBottomInVisibleScrollView = _activeField.frame.origin.y - self.scrollView.contentOffset.y + 30 /* small buffer for cursor size */;
CGPoint scrollPoint;
if (keyBoardTopInScrollView > inputFieldBottomInVisibleScrollView) {
scrollPoint = CGPointMake(0.0, self.scrollView.contentOffset.y - (keyBoardTopInScrollView - inputFieldBottomInVisibleScrollView));
} else {
scrollPoint = CGPointMake(0.0, self.scrollView.contentOffset.y + (inputFieldBottomInVisibleScrollView - keyBoardTopInScrollView));
}
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
- (void)keyboardWasShown:(NSNotification*)aNotification {
_isShowingKeyboard = YES;
NSDictionary* info = [aNotification userInfo];
_keyboardHeight = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, _keyboardHeight, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
[self adjustInputFieldsForKeyboard];
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification {
_isShowingKeyboard = NO;
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
#pragma mark UITextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField {
_activeField = textField;
if (_isShowingKeyboard) {
[self adjustInputFieldsForKeyboard];
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
_activeField = nil;
}
That's it, just call [self registerForKeyboardNotifications]; in your viewDidLoad method, hook up your scrollView outlet and textField/textView delegates in storyboard and you're all set.
The keyboard is only active when one of the texfields become active(Became first responder).So you should listen to UITextField delegate method to see when and which uitextfield became first responder.
- (void)textFieldDidBeginEditing:(UITextField *)textField
if you assign a tag to your textfield or make it ivar you can also understand which uitextfield has become active and also get its frame.
I don't use the scroll view or follow the above documentation.
Here is how without using the scroll view
In my
textFieldShouldBeginEditing
CGRect frame = self.view.frame;
frame.origin.y = <some negative value>;
self.view.frame = frame
and in my
textFieldShouldEndEditing
CGRect frame = self.view.frame;
frame.origin.y = <some positive value>; // absolute value of your above negative value
self.view.frame = frame
The above works for me. Remember these are the text field delegate methods. So you need to setup your delegate.
I have a scrollView and i want to scroll it automatically when i select an textField
(i am filling a form here)
i am using following method to scroll it
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[scrollView setContentOffset:CGPointMake(0,50*(textField.tag-1))];
}
Now the problem is that i have more than 10 text fields and when i reached to the seventh textField the scrollView scrolls more .
I also tried to print the CGPointMake()'s values...and it is showing correct values..but the scroller goes beyond the range what is expected..
Look at following images
The following 2 images showing control on textFields tag <7
But when control reaches to 7th textField it scrolls more
and after this it goes beyond bounds.
This problem occurs only when i move from one textFields to another without pressing that return button(i mean with resignFirstResponder).But when i press that return button and then go to the next field then all works fine.
Can anyone suggest where should be the problem..?
It won't scroll because it doesn't contain enough content. It will stop when the bottom edge of the content reaches the bottom end of the frame. Use something like
CGSize size = scrollView.contentSize;
scrollView.contentSize = CGSizeMake (size.width, size.height + ADDITIONAL_HEIGHT);
when setting up the scroll view, or in your -viewDidLoad: method, if it was loaded from a XIB.
It should keep the text field being edited on the screen - i.e. scroll automatically - so you shouldn't need to do anything?
However, it might be trying to edit a text field that is behind the keyboard - so the better solution would be:
//Get notifications of the keyboard opening and closing
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
- (void)keyboardWillShow:(NSNotification *)notification {
//Get the keyboard height
int h = [self.view convertRect:[[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue] toView:nil].size.height;
//Change the inset of the scroll view and scroll bars
scrollView.contentInset = scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, h, 0);
}
- (void)keyboardWillHide:(NSNotification *)notification {
scrollView.contentInset = scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, 0, 0);
}
try this.declare a variable
CGPoint svos;
in .h file and do this.
- (void)textFieldDidBeginEditing:(UITextField *)textField {
svos = scr.contentOffset;//scr is my scroll view
CGPoint pt;
CGRect rc = [textField bounds];
rc = [textField convertRect:rc toView:scr];
pt = rc.origin;
pt.x = 0;
pt.y -= 60;
[scr setContentOffset:pt animated:YES];
//NSLog(#"%f",pt.y);
}
I'm currently working on an iPhone application with a single view, which has multiple UITextFields for input. When the keyboard shows, it overlays the bottom textfields. So I added the corresponding textFieldDidBeginEditing: method, to move the view up, which works great:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if ( ( textField != inputAmount ) && ( textField != inputAge ) ) {
NSTimeInterval animationDuration = 0.300000011920929;
CGRect frame = self.view.frame;
frame.origin.y -= kOFFSET_FOR_KEYBOARD;
frame.size.height += kOFFSET_FOR_KEYBOARD;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
}
}
This method checks, if the source of the message is one of the textfields that are visible when the keyboard shows, and if not, it moves the view up.
I also added the textFieldDidEndEnditing: method, which moves the view down again (and updates some model objects according to the changed input):
- (void)textFieldDidEndEditing:(UITextField *)textField {
if ( ( textField != inputMenge ) && ( textField != inputAlter ) ) {
NSTimeInterval animationDuration = 0.300000011920929;
CGRect frame = self.view.frame;
frame.origin.y += kOFFSET_FOR_KEYBOARD;
frame.size.height -= kOFFSET_FOR_KEYBOARD;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
}
// Additional Code
}
However, this solution has a simple flaw: When I finish editing one of the "hidden" textfields and touch another textfield, the keyboard vanishes, the view moves down, the view moves up again and the keyboard reappears.
Is there any possibility to keep the keyboard from vanishing and reappearing between two edits (of the "hidden" textfields - so that the view only moves when the selected textfield changes from one that would be hidden by the keyboard to one that would not be hidden)?
This solution is based on ComSubVie's one.
Advantages:
It supports device rotation - works for all orientations;
It doesn't hardcode the values for animation duration and curve, it reads them from the keyboard notification;
It utilizes UIKeyboardWillShowNotification instead of UIKeyboardDidShowNotification to sync keyboard animation and custom actions;
It doesn't use the deprecated UIKeyboardBoundsUserInfoKey;
It handles keyboard resize due to pressing the International key;
Fixed memory leak by unregistering for keyboard events;
All keyboard handling code is encapsulated in a separate class - KBKeyboardHandler;
Flexibility - KBKeyboardHandler class may be easy extended / modified to better suit specific needs;
Limitations:
Works for iOS 4 and above, it needs small modifications to support older versions;
It works for applications with a single UIWindow. If you use multiple UIWindows, you may need to modify retrieveFrameFromNotification: method.
Usage:
Include KBKeyboardHandler.h, KBKeyboardHandler.m and KBKeyboardHandlerDelegate.h in your project. Implement the KBKeyboardHandlerDelegate protocol in your view controller - it consists of a single method, which will be called when keyboard is shown, hidden or its size is changed. Instantiate the KBKeyboardHandler and set its delegate (typically self). See sample MyViewController below.
KBKeyboardHandler.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#protocol KBKeyboardHandlerDelegate;
#interface KBKeyboardHandler : NSObject
- (id)init;
// Put 'weak' instead of 'assign' if you use ARC
#property(nonatomic, assign) id<KBKeyboardHandlerDelegate> delegate;
#property(nonatomic) CGRect frame;
#end
KBKeyboardHandler.m:
#import "KBKeyboardHandler.h"
#import "KBKeyboardHandlerDelegate.h"
#implementation KBKeyboardHandler
- (id)init
{
self = [super init];
if (self)
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
#synthesize delegate;
#synthesize frame;
- (void)keyboardWillShow:(NSNotification *)notification
{
CGRect oldFrame = self.frame;
[self retrieveFrameFromNotification:notification];
if (oldFrame.size.height != self.frame.size.height)
{
CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width,
self.frame.size.height - oldFrame.size.height);
if (self.delegate)
[self notifySizeChanged:delta notification:notification];
}
}
- (void)keyboardWillHide:(NSNotification *)notification
{
if (self.frame.size.height > 0.0)
{
[self retrieveFrameFromNotification:notification];
CGSize delta = CGSizeMake(-self.frame.size.width, -self.frame.size.height);
if (self.delegate)
[self notifySizeChanged:delta notification:notification];
}
self.frame = CGRectZero;
}
- (void)retrieveFrameFromNotification:(NSNotification *)notification
{
CGRect keyboardRect;
[[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
self.frame = [[UIApplication sharedApplication].keyWindow.rootViewController.view convertRect:keyboardRect fromView:nil];
}
- (void)notifySizeChanged:(CGSize)delta notification:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
UIViewAnimationOptions curve;
[[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve];
NSTimeInterval duration;
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration];
void (^action)(void) = ^{
[self.delegate keyboardSizeChanged:delta];
};
[UIView animateWithDuration:duration
delay:0.0
options:curve
animations:action
completion:nil];
}
#end
KBKeyboardHandlerDelegate.h:
#protocol KBKeyboardHandlerDelegate
- (void)keyboardSizeChanged:(CGSize)delta;
#end
Sample MyViewController.h:
#interface MyViewController : UIViewController<KBKeyboardHandlerDelegate>
...
#end
Sample MyViewController.m:
#implementation MyViewController
{
KBKeyboardHandler *keyboard;
}
- (void)dealloc
{
keyboard.delegate = nil;
[keyboard release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
keyboard = [[KBKeyboardHandler alloc] init];
keyboard.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
keyboard.delegate = nil;
[keyboard release];
keyboard = nil;
}
- (void)keyboardSizeChanged:(CGSize)delta
{
// Resize / reposition your views here. All actions performed here
// will appear animated.
// delta is the difference between the previous size of the keyboard
// and the new one.
// For instance when the keyboard is shown,
// delta may has width=768, height=264,
// when the keyboard is hidden: width=-768, height=-264.
// Use keyboard.frame.size to get the real keyboard size.
// Sample:
CGRect frame = self.view.frame;
frame.size.height -= delta.height;
self.view.frame = frame;
}
UPDATE: Fixed iOS 7 warning, thanks #weienv.
I just solved this problem. The solution is a combination of a UIKeyboardDidShowNotification and UIKeyboardDidHideNotification observer with the above textFieldDidBeginEditing: and textFieldDidEndEditing: methods.
You need three additional variables, one to store the current selected UITextField (which I have named activeField), one to indicate if the current view has been moved, and one to indicate if the keyboard is displayed.
This is how the two UITextField delegate methods look now:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
activeField = nil;
// Additional Code
}
When the view is loaded, the following two observers are created:
- (void)viewDidLoad {
// Additional Code
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasHidden:)
name:UIKeyboardDidHideNotification
object:nil];
}
And the corresponding methods are implemented as follows:
- (void)keyboardWasShown:(NSNotification *)aNotification {
if ( keyboardShown )
return;
if ( ( activeField != inputAmount ) && ( activeField != inputAge ) ) {
NSDictionary *info = [aNotification userInfo];
NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
NSTimeInterval animationDuration = 0.300000011920929;
CGRect frame = self.view.frame;
frame.origin.y -= keyboardSize.height-44;
frame.size.height += keyboardSize.height-44;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
viewMoved = YES;
}
keyboardShown = YES;
}
- (void)keyboardWasHidden:(NSNotification *)aNotification {
if ( viewMoved ) {
NSDictionary *info = [aNotification userInfo];
NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
NSTimeInterval animationDuration = 0.300000011920929;
CGRect frame = self.view.frame;
frame.origin.y += keyboardSize.height-44;
frame.size.height -= keyboardSize.height-44;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
viewMoved = NO;
}
keyboardShown = NO;
}
This code works now as expected. The keyboard is only dismissed when the Done button is pressed, otherwise it stays visible and the view is not moved around.
As an additional note, I think it is possible to get the animationDuration dynamically by asking the NSNotification object, since I have already played with a similar solution but didn't get it to work (which it does now).
This view controller must be UITextView Delegate and you must set self.textview.delegate = self in viewdidload
-(void) textViewDidBeginEditing:(UITextView *)textView
{
NSLog(#"%f",self.view.frame.origin.y);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25f];
CGRect frame = self.view.frame;
frame.origin.y =frame.origin.y -204;
[self.view setFrame:frame];
[UIView commitAnimations];
}
-(void) textViewDidEndEditing:(UITextView *)textView
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25f];
CGRect frame = self.view.frame;
frame.origin.y = frame.origin.y + 204;
[self.view setFrame:frame];
[UIView commitAnimations];
}
I got your Problem just do simple thing
just give outlet to UIScrollview.
set unique Tag property for each textfield in view.
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
switch (textField.tag)
{
case 2: //can be your textfiled tag
{ CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-150);
//set figure y-150 as per your comfirt
[scrollview setContentOffset:scrollPoint animated:YES];
}break;
case 3:
{ CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-180);
//set figure y-180 as per your comfirt
[scrollview setContentOffset:scrollPoint animated:YES];
}break;
...
}
}
-(void)textFieldDidEndEditing:(UITextField *)textField{
if(textField.tag==3){
[scrollview setContentOffset:CGPointZero animated:YES];
}
//set the last textfield when you want to disappear keyboard.
}
Write below code in your view controller. tbl is your table view.
-(void)viewWillAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
-(void) viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
#pragma mark - Keyboard Methods
-(void)keyboardWillShow:(NSNotification *)notification
{
// 375 × 667 ( 750 × 1334 ) iPhone 6
//414 × 736
CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height;
[UIView animateWithDuration:0.5 animations:^{
tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right);
}];
}
-(void)keyboardWillChangeFrame:(NSNotification *)notification
{
CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// int H = IS_IPHONE_5?504-keyboardRect.size.height:416-keyboardRect.size.height;
int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height;
[UIView animateWithDuration:0.5 animations:^{
// scroll.frame = rect;
tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right);
}];
}
-(void)keyboardWillHide:(NSNotification *)notification
{
[UIView animateWithDuration:0.3 animations:^{
// scroll.frame = rect;
tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, 0, tbl.contentInset.right);
}];
}
Fairly easy solution, works with all screen sizes
First you have to embed you UITextFields to a UIScrollView. In my case, I had several UITextFields and a UITextView.
Then you have to inherit from UITextFieldDelegate, UITextViewDelegate.
class SettingsVC: UIViewController, UITextFieldDelegate, UITextViewDelegate
Assign textfield's and textview's delegates to self.
fullNameTextField.delegate = self
usernameTextField.delegate = self
websiteTextField.delegate = self
profileDescription.delegate = self
Then use this code:
var editingTextInput: UIView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardShown(notification:)),
name: NSNotification.Name.UIKeyboardDidShow,
object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)
}
func keyboardShown(notification: NSNotification) {
if let infoKey = notification.userInfo?[UIKeyboardFrameEndUserInfoKey],
let rawFrame = (infoKey as AnyObject).cgRectValue {
let keyboardFrame = view.convert(rawFrame, to: view)
let editingTextInputFrame = self.editingTextInput.convert(self.editingTextInput.frame, to: view)
if editingTextInputFrame.maxY > keyboardFrame.minY{
let diff = keyboardFrame.minY - editingTextInputFrame.maxY
containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true)
}
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
self.editingTextInput = textField
}
func textViewDidBeginEditing(_ textView: UITextView) {
self.editingTextInput = textView
}
func textFieldDidEndEditing(_ textField: UITextField) {
containerScrollView.setContentOffset(CGPoint.zero, animated: true)
}
func textViewDidEndEditing(_ textView: UITextView) {
containerScrollView.setContentOffset(CGPoint.zero, animated: true)
}
In short, you subscribe to UIKeyboardDidShow notification.
When you tap on textField or textView keyboard is shown and you grab keyboard's frame and frame of input element you have tapped on. Convert them to viewController's coordinate system and compare input element's lowest point to a keyboard's highest. If element's lower part is lower than keyboard's highest, than set offset of containerScrollView to the difference between them.
if editingTextInputFrame.maxY > keyboardFrame.minY{
let diff = keyboardFrame.minY - editingTextInputFrame.maxY
containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true)
}
Like I mentioned in this answer:
I've developed a framework for my own need to solve this issue better, and made it public now. It's not just for UITextField and UITextView, It works for any custom UIView that adopts UITextInput protocol like UITextField and UITextView and offers many useful features. You can install it via Carthage, CocoaPods or Swift Package Manager.
ODScrollView GitHub
ODScrollView Medium
ODScrollView is just a UIScrollView that automatically moves editable text areas like UITextField and UITextView vertically depending on keyboard visibility to offer better user experience.
Features
Automatically moves first responder UIViews that adopt the UITextInput protocol up/down when the keyboard appears/disappears, e.g., UITextField, UITextView, UISearchTextField or any custom UIView that adopts UITextInput protocol.
Note that if UITextInput's frame does NOT fits the remaining area between ODScrollView and keyboard, then ODScrollView adjusts UITextInput based on cursor position instead of frame. In such cases, "trackTextInputCursor" feature can be used. Example
Adjustment margin can be applied for each UITextInput seperately for .Top and .Bottom adjustment direction setting. 20 CGFloat by default.
Adjustment can be enabled/disabled for each UITextInput seperately. true by default.
Adjustment directon - .Top, .Center, .Bottom - can be applied for each UITextInput seperately. .Bottom by default. Example
Adjustment options determines how ODScrollView adjusts. .Always by default.
.Always : ODScrollView always adjusts the UITextInput which is placed anywhere in the ODScrollView regardless UITextInput overlaps or not with shown keyboard. Example
.IfNeeded : ODScrollView only adjusts the UITextInput if it overlaps with the shown keyboard. Example
Besides UIScrollView.keyboardDismissModes, the keyboard can be dismissed by tapping a UIView which is provided by ODScrollViewDelegate. After the keyboard is dismissed, ODScrollView can return its original position. nil and false by default. Example
Usage
1 - First thing you need to do is setting up ODScrollView and its content view properly. Since ODScrollView is just a UIScrollView, you can implement ODScrollView same way you do for UIScrollView. It's up to you to create ODScrollView by using storyboard or programmatically.
If you're creating ODScrollView programmatically, you can continue from step 4.
Suggested way to create UIScrollView in Storyboard
- If you are using Content Layout Guide and Frame Layout Guide:
1.1 - scrollView: Place UIScrollView anywhere you want to use.
1.2 - contentView: Place UIView inside scrollView.
1.3 - Set contentView's top, bottom, leading and trailing constraints to Content Layout Guide's constraints.
1.4 - Set contentView's width equal to Frame Layout Guide's width.
1.5 - Set contentView's height equal to Frame Layout Guide's height or set static height which is larger than scrollView's height.
1.6 - Build your UI inside contentView.
- If you are NOT using Content Layout Guide and Frame Layout Guide:
1.1 - scrollView: Place UIScrollView anywhere you want to use.
1.2 - contentView: Place UIView inside scrollView.
1.3 - Set contentView's top, bottom, leading and trailing constraints to 0.
1.4 - Set contentView's width equal to scrollView's width.
1.5 - Set contentView's height equal to scrollView's superview's height or set static height which is larger than scrollView's height.
1.6 - Build your UI inside contentView.
2 - Change the scrollView's class from UIScrollView to ODScrollView in the identity inspector on Storyboard.
3 - Create IBOutlets for scrollView and contentView on ViewController.
4 - Call the following methods inside ViewDidLoad() on ViewController:
override func viewDidLoad() {
super.viewDidLoad()
//ODScrollView setup
scrollView.registerContentView(contentView)
scrollView.odScrollViewDelegate = self
}
5 - Optional: You still can use UIScrollView's features:
override func viewDidLoad() {
super.viewDidLoad()
//ODScrollView setup
scrollView.registerContentView(contentView)
scrollView.odScrollViewDelegate = self
// UIScrollView setup
scrollView.delegate = self // UIScrollView Delegate
scrollView.keyboardDismissMode = .onDrag // UIScrollView keyboardDismissMode. Default is .none.
UITextView_inside_contentView.delegate = self
}
6 - Adopt ODScrollViewDelegate from ViewController and decide ODScrollView options:
extension ViewController: ODScrollViewDelegate {
// MARK:- State Notifiers: are responsible for notifiying ViewController about what is going on while adjusting. You don't have to do anything if you don't need them.
// #Optional
// Notifies when the keyboard showed.
func keyboardDidShow(by scrollView: ODScrollView) {}
// #Optional
// Notifies before the UIScrollView adjustment.
func scrollAdjustmentWillBegin(by scrollView: ODScrollView) {}
// #Optional
// Notifies after the UIScrollView adjustment.
func scrollAdjustmentDidEnd(by scrollView: ODScrollView) {}
// #Optional
// Notifies when the keyboard hid.
func keyboardDidHide(by scrollView: ODScrollView) {}
// MARK:- Adjustment Settings
// #Optional
// Specifies the margin between UITextInput and ODScrollView's top or bottom constraint depending on AdjustmentDirection
func adjustmentMargin(for textInput: UITextInput, inside scrollView: ODScrollView) -> CGFloat {
if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
return 20
} else {
return 40
}
}
// #Optional
// Specifies that whether adjustment is enabled or not for each UITextInput seperately.
func adjustmentEnabled(for textInput: UITextInput, inside scrollView: ODScrollView) -> Bool {
if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
return true
} else {
return false
}
}
// Specifies adjustment direction for each UITextInput. It means that some of UITextInputs inside ODScrollView can be adjusted to the bottom, while others can be adjusted to center or top.
func adjustmentDirection(selected textInput: UITextInput, inside scrollView: ODScrollView) -> AdjustmentDirection {
if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
return .bottom
} else {
return .center
}
}
/**
- Always : ODScrollView always adjusts the UITextInput which is placed anywhere in the ODScrollView.
- IfNeeded : ODScrollView only adjusts the UITextInput if it overlaps with the shown keyboard.
*/
func adjustmentOption(for scrollView: ODScrollView) -> AdjustmentOption {
.Always
}
// MARK: - Hiding Keyboard Settings
/**
#Optional
Provides a view for tap gesture that hides keyboard.
By default, keyboard can be dismissed by keyboardDismissMode of UIScrollView.
keyboardDismissMode = .none
keyboardDismissMode = .onDrag
keyboardDismissMode = .interactive
Beside above settings:
- Returning UIView from this, lets you to hide the keyboard by tapping the UIView you provide, and also be able to use isResettingAdjustmentEnabled(for scrollView: ODScrollView) setting.
- If you return nil instead of UIView object, It means that hiding the keyboard by tapping is disabled.
*/
func hideKeyboardByTappingToView(for scrollView: ODScrollView) -> UIView? {
self.view
}
/**
#Optional
Resets the scroll view offset - which is adjusted before - to beginning its position after keyboard hid by tapping to the provided UIView via hideKeyboardByTappingToView.
## IMPORTANT:
This feature requires a UIView that is provided by hideKeyboardByTappingToView().
*/
func isResettingAdjustmentEnabled(for scrollView: ODScrollView) -> Bool {
true
}
}
7 - Optional: You can adjust the ODScrollView when the cursor overlaps with keyboard while typing in multiline UITextInput. trackTextInputCursor(for UITextInput) must be called by UITextInput functions that is fired while typing.
/**
## IMPORTANT:
This feature is not going to work unless textView is subView of _ODScrollView
*/
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
_ODScrollView.trackTextInputCursor(for textView)
return true
}