Generic UITableView keyboard resizing algorithm - iphone

I've searched a lot for code that resizes the table view to accomodate for keyboard showing and hiding, but almost every single post i came across assumes that the table view is taking the entire view of its view controller. I have an iPad application where the table view is only taking part of the screen. What's the correct way to resize the table view in this case? (all the code in the posts i've mentioned above fails)

The following code does what you want and works with any device and any layout. The code is courtesy of the Sensible TableView framework (with permission to copy and use).
- (void)keyboardWillShow:(NSNotification *)aNotification
{
if(keyboardShown)
return;
keyboardShown = YES;
// Get the keyboard size
UIScrollView *tableView;
if([self.tableView.superview isKindOfClass:[UIScrollView class]])
tableView = (UIScrollView *)self.tableView.superview;
else
tableView = self.tableView;
NSDictionary *userInfo = [aNotification userInfo];
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [tableView.superview convertRect:[aValue CGRectValue] fromView:nil];
// Get the keyboard's animation details
NSTimeInterval animationDuration;
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
UIViewAnimationCurve animationCurve;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
// Determine how much overlap exists between tableView and the keyboard
CGRect tableFrame = tableView.frame;
CGFloat tableLowerYCoord = tableFrame.origin.y + tableFrame.size.height;
keyboardOverlap = tableLowerYCoord - keyboardRect.origin.y;
if(self.inputAccessoryView && keyboardOverlap>0)
{
CGFloat accessoryHeight = self.inputAccessoryView.frame.size.height;
keyboardOverlap -= accessoryHeight;
tableView.contentInset = UIEdgeInsetsMake(0, 0, accessoryHeight, 0);
tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, accessoryHeight, 0);
}
if(keyboardOverlap < 0)
keyboardOverlap = 0;
if(keyboardOverlap != 0)
{
tableFrame.size.height -= keyboardOverlap;
NSTimeInterval delay = 0;
if(keyboardRect.size.height)
{
delay = (1 - keyboardOverlap/keyboardRect.size.height)*animationDuration;
animationDuration = animationDuration * keyboardOverlap/keyboardRect.size.height;
}
[UIView animateWithDuration:animationDuration delay:delay
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{ tableView.frame = tableFrame; }
completion:^(BOOL finished){ [self tableAnimationEnded:nil finished:nil contextInfo:nil]; }];
}
}
- (void)keyboardWillHide:(NSNotification *)aNotification
{
if(!keyboardShown)
return;
keyboardShown = NO;
UIScrollView *tableView;
if([self.tableView.superview isKindOfClass:[UIScrollView class]])
tableView = (UIScrollView *)self.tableView.superview;
else
tableView = self.tableView;
if(self.inputAccessoryView)
{
tableView.contentInset = UIEdgeInsetsZero;
tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
if(keyboardOverlap == 0)
return;
// Get the size & animation details of the keyboard
NSDictionary *userInfo = [aNotification userInfo];
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [tableView.superview convertRect:[aValue CGRectValue] fromView:nil];
NSTimeInterval animationDuration;
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
UIViewAnimationCurve animationCurve;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
CGRect tableFrame = tableView.frame;
tableFrame.size.height += keyboardOverlap;
if(keyboardRect.size.height)
animationDuration = animationDuration * keyboardOverlap/keyboardRect.size.height;
[UIView animateWithDuration:animationDuration delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{ tableView.frame = tableFrame; }
completion:nil];
}
- (void) tableAnimationEnded:(NSString*)animationID finished:(NSNumber *)finished contextInfo:(void *)context
{
// Scroll to the active cell
if(self.activeCellIndexPath)
{
[self.tableView scrollToRowAtIndexPath:self.activeCellIndexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
[self.tableView selectRowAtIndexPath:self.activeCellIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
Notes:
a. The above two methods have been added to the notification center using the following code:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
b. The ivars used above has been declared like this:
BOOL keyboardShown;
CGFloat keyboardOverlap;
c. 'self.activeCellIndexPath' is always set to the indexPath of the cell owning the currently active UITextField/UITextView.
Enjoy! :)

I've found the easiest solution to be this one(I'm not fan of using subviews for this kind of stuff):
register for keyboard frame change notification(idealy register in viewWillAppear: and unregister in viewWillDisappear:):
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
and then in method:
- (void)keyboardDidChangeFrame:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect kbIntersectFrame = [window convertRect:CGRectIntersection(window.frame, kbFrame) toView:self.scrollView];
kbIntersectFrame = CGRectIntersection(self.bounds, kbIntersectFrame);
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbIntersectFrame.size.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
or if You want to get rid of the "jump" after changing contentInset:
- (void)keyboardDidChangeFrame:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect kbIntersectFrame = [window convertRect:CGRectIntersection(window.frame, kbFrame) toView:self.scrollView];
kbIntersectFrame = CGRectIntersection(self.scrollView.bounds, kbIntersectFrame);
// get point before contentInset change
CGPoint pointBefore = self.scrollView.contentOffset;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbIntersectFrame.size.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// get point after contentInset change
CGPoint pointAfter = self.scrollView.contentOffset;
// avoid jump by settings contentOffset
self.scrollView.contentOffset = pointBefore;
// and now animate smoothly
[self.scrollView setContentOffset:pointAfter animated:YES];
}

The simple solution is to add my extension UIViewController+Keyboard.swift to your project, with a single line setupKeyboardNotifcationListenerForScrollView(tableView) it will auto resize automatically. No need to subclass anything, just an extension! Its open source at SingleLineKeyboardResize

Simple solution - register to receive keyboard notifications on init or viewDidLoad with:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
then when the notification is received, you can use the given rect of the keyboard to adjust the frame of your tableView:
- (void)keyboardWillShow:(NSNotification *)notification
{
// Get the size of the keyboard.
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect newTableFrame = _myTableView.frame;
//Here make adjustments to the tableview frame based on the value in keyboard size
...
_myTableView.frame = newTableFrame;
}
- (void)keyboardWillHide:(NSNotification *)notification
{
//Here change the table frame back to what it originally was.
}

Check out this project, it's drag and drop so just declare the tablview as type TPKeyboardAvoidingTableView

Here is the keyboard method:
func keyboardControl(notification: NSNotification, isShowing: Bool) {
var userInfo = notification.userInfo!
let keyboardRect = userInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue
let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey]!.unsignedIntValue
let convertedFrame = self.view.convertRect(keyboardRect, fromView: nil)
let heightOffset = self.view.bounds.size.height - convertedFrame.origin.y
let options = UIViewAnimationOptions(rawValue: UInt(curve) << 16)
let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey]!.doubleValue
//your UITableView bottom constrant
self.tableViewMarginBottomConstraint.constant = heightOffset
var contentInsets = UIEdgeInsetsZero
if isShowing {
contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardRect.size.height), 0.0)
}
UIView.animateWithDuration(
duration,
delay: 0,
options: options.union(.LayoutSubviews).union(.BeginFromCurrentState),
animations: {
self.listTableView.contentInset = contentInsets
self.listTableView.scrollIndicatorInsets = contentInsets
self.listTableView.scrollBottomToLastRow()
self.view.layoutIfNeeded()
},
completion: { bool in
})
}
Here is the UITableView extension:
extension UITableView {
func totalRows() -> Int {
var i = 0
var rowCount = 0
while i < self.numberOfSections {
rowCount += self.numberOfRowsInSection(i)
i++
}
return rowCount
}
var lastIndexPath: NSIndexPath {
get { return NSIndexPath(forRow: self.totalRows()-1, inSection: 0) }
}
func scrollBottomToLastRow() {
self.scrollToRowAtIndexPath(self.lastIndexPath, atScrollPosition: .Bottom, animated: false)
}
}

You can achieve what you're looking for by using IQKeyboardManager, it's a codeless library, you just have to add it to your Podfile: pod 'IQKeyboardManager' and that's it, it will hand the scrolling effect when the keyboard is shown even if the UITextField/UITextView is not part of a scrollView/tableView.

Related

Why does the view scroll up when keyboard is present for a textview but not for a textfield?

I have a textfield and a textview in a scroll view. When I begin to edit the textview the view scrolls up appropriately so the keyboard does not hide the text. The textfield, however, does not scroll up but is hidden by the keyboard. I have set the delegates of the textfields and textviews to the view controllers. The code I have so far is below. Thanks for the help in advance.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
activeField = nil;
}
- (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)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
kbRect = [self.view convertRect:kbRect toView:nil];
CGSize kbSize = kbRect.size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
pageScrollView.contentInset = contentInsets;
pageScrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[pageScrollView setContentOffset:scrollPoint animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
pageScrollView.contentInset = contentInsets;
pageScrollView.scrollIndicatorInsets = contentInsets;
}
michael tyson created TPKeyboardAvoiding which is very easy to use you can get it from github. https://github.com/michaeltyson/TPKeyboardAvoiding
I do not know why it works for your textview, but for a scrollview with textfields I am doing this manually via
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWillBeShown:(NSNotification*)aNotification
{
if(keyboardShown) return;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* keyboardFrameValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRectWrtScreen = [keyboardFrameValue CGRectValue];
CGRect keyboardRectWrtView = [scrollView convertRect:[[scrollView window] convertRect:keyboardRectWrtScreen fromWindow:nil] fromView: nil];
float keyboardHeight = keyboardRectWrtView.size.height;
scrollView.frame = CGRectMake(scrollView.frame.origin.x, scrollView.frame.origin.y, scrollView.frame.size.width,scrollView.frame.size.height-keyboardHeight);
if(activeTextField) {
CGRect rect = [contentView convertRect:[activeTextField bounds] fromView:activeTextField];
rect.origin.y -= 25;
rect.size.height += 50;
[scrollView scrollRectToVisible:rect animated:YES];
}
[UIView commitAnimations];
keyboardShown = YES;
}
as you see, I am manually moving the scroll view to the visible part, then ensuring that the text field is in the center of the scroll view.

Scrolling UITextField into view when keyboard is shown

I'm writing an app targeting iOS 6.1. I'm trying to follow this Apple doc on how to scroll a UITextField into view if it is obscured by the keyboard. The problem is that Apple's documented algorithm for calculating the scroll point doesn't work so well. My algorithm for calculating the scroll point works only slightly better, but is off by 70 pixels. What is the correct way to calculate the scroll point?
Below you can see, from left to right, my view before the keyboard is shown, my view after scrolling using Apple's algorithm to calculate the scroll point, and my view after scrolling using my algorithm to calculate the scroll point. (Each square in that grid is 25 pixels by 25 pixels.)
And here is the code I am using. Note the #if APPLE_WAY block.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.view.contentInset = contentInsets;
self.view.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.activeField.frame.origin) ) {
#if APPLE_WAY
CGPoint scrollPoint = CGPointMake(0.0, self.activeField.frame.origin.y-kbSize.height);
#else
CGFloat offset = self.activeField.frame.origin.y;
//TODO: Why is this off by 70?
offset = offset - 70;
CGPoint scrollPoint = CGPointMake(0.0, offset);
#endif
[self.view setContentOffset:scrollPoint animated:YES];
}
}
You simply need to set the offset to the height of the keyboard:
CGFloat offset = kbSize.height;
CGPoint scrollPoint = CGPointMake(0.0, offset);
[self.view setContentOffset:scrollPoint animated:YES];
hope this helps
There are a few ways to do this. Given your situation, I would do something like this:
- (void)keyboardWasShown:(NSNotification *)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, self.activeField.frame.origin.y-kbSize.height);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification *)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0, 0);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}
Then, in your viewDidLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
It's easy when what you've got is a scroll view, because you don't even have to scroll; all you have to do is adjust the contentInset and scrollIndicatorInsets to get out of the keyboard's way, and the rest happens automatically:
- (void) keyboardShow: (NSNotification*) n {
self->_oldContentInset = self.scrollView.contentInset;
self->_oldIndicatorInset = self.scrollView.scrollIndicatorInsets;
self->_oldOffset = self.scrollView.contentOffset;
NSDictionary* d = [n userInfo];
CGRect r = [[d objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
r = [self.scrollView convertRect:r fromView:nil];
CGRect f = self.fr.frame;
CGFloat y =
CGRectGetMaxY(f) + r.size.height -
self.scrollView.bounds.size.height + 5;
if (r.origin.y < CGRectGetMaxY(f))
[self.scrollView setContentOffset:CGPointMake(0, y) animated:YES];
UIEdgeInsets insets;
insets = self.scrollView.contentInset;
insets.bottom = r.size.height;
self.scrollView.contentInset = insets;
insets = self.scrollView.scrollIndicatorInsets;
insets.bottom = r.size.height;
self.scrollView.scrollIndicatorInsets = insets;
}
I give a bunch of different strategies, with code, in this section of my book:
http://www.apeth.com/iOSBook/ch23.html#_keyboard_covers_text_field
All of that is backed by downloadable projects that you can grab at my github site.

Slide view upon TextField selection is only on typing

I have used the demonstration code Apple has in their docs here:
http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html#//apple_ref/doc/uid/TP40009542-CH5-SW7
I need to slide the view up when the keyboard appears from tapping a UITextField. I am using the code in the link above, Apple's own demo code for ding this and it makes sense.
However, my issue, using Apple's code virtually unchanged, the view only slides up when typing, vs actually after tapping on the text field.
In the - (void)keyboardWasShown:(NSNotification*)aNotification method, it is when this if statement is called that the view is slid up or not. Slid up if the textfield is below the keyboard, not slid up if it isn't below the keyboard.
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
Trouble is, upon tapping the text field, this if statement is skipped, the code inside it doesn't run.
I can't work it out, but it all happens in this method below. Why is it only sliding up on typing and not the initial tap?
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
}
I looked again at the Apple doc. Yuck - don't they write software for a living?? Try this instead: Remember the default position of the main view.
#interface ViewController ()
#property (assign, nonatomic) CGFloat viewOriginY;
#end
Register for keyboard notification and a tap (to dismiss first responder).
- (void)viewDidLoad
{
[super viewDidLoad];
self.viewOriginY = self.view.frame.origin.y;
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[center addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
// add a tap gesture to drop first responder
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[self.view addGestureRecognizer:tapGR];
}
Write a function to (recursively) find the first responder.
- (UIView *)firstResponderWithin:(UIView *)view {
if ([view isFirstResponder]) return view;
for (UIView *subview in view.subviews) {
UIView *answer = [self firstResponderWithin:subview];
if (answer) return answer;
}
return nil;
}
Here's the punchline: When the keyboard is shown, compute it's frame and the frame of the view that needs to be visible. Important to do this in a common coordinate system, like this vc's view. Then slide the view to make the first responder visible.
- (void)keyboardDidShow:(NSNotification *)notification {
CGRect keyboardFrameW = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
CGRect keyboardFrame = [window convertRect:keyboardFrameW toView:self.view];
UIView *firstResponder = [self firstResponderWithin:self.view];
CGRect firstResponderFrame = [firstResponder.superview convertRect:firstResponder.frame toView:self.view];
// let's put the bottom of the first responder's frame just above the top of the keyboard
CGFloat firstResponderBottom = CGRectGetMaxY(firstResponderFrame);
CGFloat targetBottom = keyboardFrame.origin.y - 8.0;
CGFloat offsetY = MAX(0.0, firstResponderBottom - targetBottom);
[UIView animateWithDuration:0.3 animations:^{
self.view.frame = CGRectOffset(self.view.frame, 0.0, -offsetY);
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
[UIView animateWithDuration:0.3 animations:^{
self.view.frame = CGRectMake(0.0, self.viewOriginY, self.view.frame.size.width, self.view.frame.size.height);
}];
}
Optional - When user taps anyplace else, dismiss the keyboard.
- (void)tapped:(UITapGestureRecognizer *)gr {
UIView *firstResponder = [self firstResponderWithin:self.view];
[firstResponder resignFirstResponder];
}
Working project can be found here.
I think this idea is one ivar away from a nice category on view controller. You could import it, and invoke the setup on viewDidLoad. All it needs is a way to get the main view's default y origin without writing it in an ivar. I need to noodle on that.

avoid view when opening keyboard from a UITextView inside a UITableView with custom cell

I have a UIViewController that contains a UITableView with custom cells, inside the cell are UILabels, a couple of uneditable UITextView and one editable UITextView. Now, when I tap on one of the UITextView that is near the bottom or the bottom part of the table, the UITextView is covered by the keyboard. I've tried http://cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html which works great for textfield/textview but not working on the table with custom cell. Any help or suggestions how to go about this?
When your table view contains data entry fields like a UITextField or a UITextView and the table view is long enough to cover the screen, you will have a problem accessing data entry fields that are hidden by the keyboard.
To overcome this problem two solutions are:
The easiest and recommended way is to use a UITableViewController instead of UIViewController, which automatic make sure keypad won't hide the editable field (If possible use this approach to avoid U.I. adjustment inconvenience)
If you use a UIViewController and a UITableView as its subview. You can scroll your UI’s frame by observing the UIKeyboardWillShowNotification and UIKeyboardWillHideNotification
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil]; //Posted immediately prior to the display of the keyboard
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:nil]; //Posted immediately prior to the dismissal of the keyboard.
}
- (void)keyboardWillShow:(NSNotification *)aNotification
{
CGRect keyboardBounds = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardBounds.size.height, 0); //when keyboard is up, that time just bring your text filed above the keyboard
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, keyboardBounds.size.height, 0);
[self.tableView scrollToRowAtIndexPath:[self findIndexPathToScroll]
atScrollPosition:UITableViewScrollPositionTop
animated:YES]; //findIndexPathToScroll implementation not shown
[UIView commitAnimations];
}
- (void)keyboardWillHide:(NSNotification *)aNotification
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
self.tableView.contentInset = UIEdgeInsetsZero; //Once keyboard is hidden then bring back your table into your original position.
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
[UIView commitAnimations];
}
registerForKeyboardNotifications - call this method when you load the UITableView, ie: viewDidLoad
findIndexPathToScroll - (Implementation not shown) Its your business logic to prepare IndexPath where table view should scroll
removeObserver 'UIKeyboardWillShowNotification' and 'UIKeyboardWillHideNotification' both in dealloc and viewDidUnload
I fixed the issue. Please see my solution below:
1. First declare a global varibale called "activeFileld"
#property(nonatomic,strong)id activeFiled;
2. Create a method called "registerForKeyboardNotifications"
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil]; //Posted immediately prior to the display of the keyboard
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:nil]; //Posted immediately prior to the dismissal of the keyboard.
}
3. Called the above method in viewWillAppear:
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//Register kryboard Notification
[self registerForKeyboardNotifications];
}
4. Call the Delegate method for UitextFieldd Or UitextView
- (void)textFieldDidBeginEditing:(UITextField *)sender {
self.activeField = sender;
}
- (void)textFieldDidEndEditing:(UITextField *)sender{
self.activeField = nil;
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
// save the text view that is being edited
_notes = textView.text;
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
// release the selected text view as we don't need it anymore
_activeField = nil;
}
5.
- (void)keyboardWillShow:(NSNotification *)notification
{
if([_activeField isKindOfClass:[UITextField class]]) {
NSDictionary* info = [notification userInfo];
NSLog(#"Dictionary %#",info);
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
kbRect = [self.view convertRect:kbRect fromView:nil];
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbRect.size.height, 0.0);
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
CGRect aRect = self.view.frame;
aRect.size.height -= kbRect.size.height;
UITextField *textField = (UITextField*)_activeField;
if (!CGRectContainsPoint(aRect, textField.frame.origin) ) {
[self.tableView scrollRectToVisible:textField.frame animated:YES];
}
}else if([_activeField isKindOfClass:[UITextView class]]) {
NSDictionary* info = [notification userInfo];
NSLog(#"Dictionary %#",info);
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
kbRect = [self.view convertRect:kbRect fromView:nil];
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbRect.size.height, 0.0);
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
CGRect aRect = self.view.frame;
aRect.size.height += kbRect.size.height;
UITextView *activeTextView = (UITextView*)_activeField;
if (!CGRectContainsPoint(aRect, textField.superview.superview.frame.origin) ) {
[self.tableView scrollRectToVisible:activeTextView.superview.superview.frame animated:YES];
}
}
}
// Called when the UIKeyboardWillHideNotification is received
- (void)keyboardWillHide:(NSNotification *)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
}
Two solutions:
Preferred: use a UITableViewController instead of a UIViewController as that one will automatically make sure that your keypad won't hide the editable field.
Hacky: How to make a UITextField move up when keyboard is present?
A simple solution. Implement the heightForFooter method, and let it return a value of (say) 100, and when you select the a cell in UITableView, they will simply slide up by that height, and the keyboard will not cover the view.
I've always used a two fold solution for this.
Resize table so it now fits in the smaller area.
Scroll to the cell we want visible. (we needed to re-size the table for this or you'd still wind up being unable to get to the last couple of cells in the table.)
To do this, I register keyboard show/hide events and act accordingly when they get called.
- (void)keyboardWillShow:(NSNotification *)note {
[self updateForKeyboardShowHide:note appearing:YES];
}
- (void)keyboardWillHide:(NSNotification *)note {
[self updateForKeyboardShowHide:note appearing:NO];
}
- (void)updateForKeyboardShowHide:(NSNotification *)note appearing:(BOOL)isAppearing {
// ignore notifications if our view isn't attached to the window
if (self.view.window == nil)
return;
CGFloat directionalModifier = isAppearing?-1:1;
CGRect keyboardBounds = [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGFloat animationDuration = [[note.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
// figure out table re-size based on keyboard
CGFloat keyboardHeight;
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsPortrait(orientation))
keyboardHeight = keyboardBounds.size.height;
else
keyboardHeight = keyboardBounds.size.width;
[UIView animateWithDuration:animationDuration animations:^{
// resize table
CGRect newFrame = table.frame;
newFrame.size.height += [self calculateKeyboardOffsetWithHeight:keyboardHeight] * directionalModifier;
table.frame = newFrame;
} completion:^(BOOL finished){
// scroll to selected cell
if (isAppearing) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:textFieldInEdit.tag inSection:0];
[table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}];
}
- (CGFloat)calulateKeyboardOffsetWithHeight:(CGFloat)keyboardHeight {
// This depends on the size and position of your table.
// If your table happen to go all the way to the bottom of
// the screen, you'll needs to adjust it's size by the whole keyboard height.
// You might as well ditch this method and inline the value.
return keyboardHeight;
// My table did not go to the bottom of the screen and the position was
// change dynamically so and there was long boring calculation I needed to
// do to figure out how much my table needed to shrink/grow.
}

How do I get UITextView to scroll properly when the keyboard is visible

I have a UITextView sitting on top of a UIView, and if I tap on it to open it for editing, then the keyboard is blocking the bottom of the view and I can not see it even though I can write in this area. Can I tell the UITextView to have a different scroll area or what is the solution?
A better solution, specially for iOS 7, would be to adjust the content inset property of the textview instead of its frame, this way, the keyboard will blur the text that falls behinds it like in any other iOS 7 app. You'll also have to adjust the scroll indicators to match.
Expanding Lindemann's answer,
- (void)keyboardWasShown:(NSNotification*)notification {
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
self.textView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
self.textView.scrollIndicatorInsets = self.textView.contentInset;
}
- (void)keyboardWillBeHidden:(NSNotification*)notification {
self.textView.contentInset = UIEdgeInsetsZero;
self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
An easy solution is to implement the UITextViewDelegate Methods
- (void)textViewDidBeginEditing:(UITextView *)textView
and
- (void)textViewDidEndEditing:(UITextView *)textView
You can make the UITextView Frame smaller when the keyboard appears and make it full size again when the keyboard disappears...like this:
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height/1.8);
}
- (void)textViewDidEndEditing:(UITextView *)textView {
self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
EDIT
The solution above is bad...don't use it!!!
Now I think it's a better idea to resize the UITextView in proportion to the keyboard size and not with a fixed value...because the size of the keyboard can change when an other language become chosen or the device become rotated...of course -.-
At first you must register your UIViewController which displays your UITextView for receiving Keyboard Notifications:
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
Then you have to implement the two methods -keyboardWasShown: and
-keyboardWillBeHidden:.
The size of the actual keyboard is contained in the NSNotification object.
- (void)keyboardWasShown:(NSNotification*)notification {
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - keyboardSize.height);
}
- (void)keyboardWillBeHidden:(NSNotification*)notification {
self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
If you want a Messages App style input, you can use a nested UITextView (allows for multiple lines of text). It will look like this in the end:
You start by laying out a view to hold all the child views. Here the background colour of the bottomView is set to match UIKeyboardAppearanceDark. It rests at the bottom of the screen.
bottomView = [UIView new];
bottomView.frame = CGRectMake(0, h-45, w, 45);
bottomView.backgroundColor = [UIColor colorWithRed:0.078 green:0.078 blue:0.078 alpha:1];
[self.view addSubview:bottomView];
Then, add in a simple background view styled like a typical UITextField, and add the UITextView as a subview to that. The inputTV (UITextView) takes its height based upon the size of the font. Also, all the padding is removed from inputTV using the textContainer variables.
inputTVBG = [UIImageView new];
inputTVBG.frame = CGRectMake(10, 8, w-90, 29);
inputTVBG.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.1f];
inputTVBG.layer.cornerRadius = 4.0f;
inputTVBG.userInteractionEnabled = true;
inputTVBG.clipsToBounds = true;
[bottomView addSubview:inputTVBG];
inputTV = [UITextView new];
inputTV.font = [UIFont systemFontOfSize:14.0f];
inputTV.frame = CGRectMake(5, 6, w-100, inputTV.font.lineHeight);
inputTV.backgroundColor = [UIColor clearColor];
inputTV.keyboardAppearance = UIKeyboardAppearanceDark;
inputTV.delegate = self;
inputTV.autocorrectionType = UITextAutocorrectionTypeNo;
inputTV.tintColor = [UIColor whiteColor];
inputTV.textColor = [UIColor whiteColor];
inputTV.textContainer.lineFragmentPadding = 0;
inputTV.textContainerInset = UIEdgeInsetsZero;
[inputTVBG addSubview:inputTV];
In the example above, I've included a label indicating how many letters are left (max / min characters) and a submit button.
lettersLeftLabel = [UILabel new];
lettersLeftLabel.frame = CGRectMake(w-70, 8, 60, 16);
lettersLeftLabel.font = [UIFont systemFontOfSize:12.0f];
lettersLeftLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5f];
lettersLeftLabel.alpha = 0.0f;
[bottomView addSubview:lettersLeftLabel];
submitButton = [UIButton new];
submitButton.frame = CGRectMake(w-70, 0, 60, 45);
[submitButton setTitle:#"SUBMIT" forState:UIControlStateNormal];
[submitButton setTitleColor:[_peacock.applePink colorWithAlphaComponent:0.5f] forState:UIControlStateNormal];
[submitButton addTarget:self action:#selector(submit) forControlEvents:UIControlEventTouchUpInside];
[submitButton.titleLabel setFont:[UIFont boldSystemFontOfSize:14.0f]];
[bottomView addSubview:submitButton];
Add this line early on in your code, so you get keyboard change updates:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
It calls the method below when a user clicks on the inputTV. Here it sets the variable 'keyboardHeight' used later on.
-(void)keyboardWillShow:(NSNotification *)n {
CGRect rect = [n.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [self.view convertRect:rect fromView:nil];
keyboardHeight = keyboardFrame.size.height;
[self textViewDidChange:inputTV];
}
This is the main bit of code that takes care of all the movement and resizing of the inputTV.
-(void)textViewDidChange:(UITextView *)textView {
//1. letters and submit button vars
int numberOfCharacters = (int)textView.text.length;
int minCharacters = 50;
int maxCharacters = 400;
int remainingCharacters = maxCharacters-numberOfCharacters;
//2. if entered letters exceeds maximum, reset text and return
if (remainingCharacters <= 0){
textView.text = [textView.text substringToIndex:maxCharacters];
numberOfCharacters = maxCharacters;
}
//3. set height vars
inputTV.scrollEnabled = true;
float textHeight = textView.contentSize.height;
float lineHeight = roundf(textView.font.lineHeight);
float additionalHeight = textHeight - lineHeight;
float moveUpHeight = keyboardHeight + additionalHeight;
//4. default letter colour is weak white
UIColor * letterColour = [[UIColor whiteColor] colorWithAlphaComponent:0.5f];
if (numberOfCharacters < minCharacters){ //minimum threshold not met
lettersLeftLabel.text = [NSString stringWithFormat:#"%i", minCharacters-numberOfCharacters];
letterColour = [_peacock.applePink colorWithAlphaComponent:0.5f];
} else { //within range
lettersLeftLabel.text = [NSString stringWithFormat:#"%i/%i", numberOfCharacters, maxCharacters];
if (remainingCharacters<5){ //increase alpha towards the end of range
letterColour = [[UIColor whiteColor] colorWithAlphaComponent:1.0f - ((float)remainingCharacters/10)];
}
}
//5. hide/show letter label based on textView height
float letterAlpha = 0.0f; //default hide
if (additionalHeight > 0){ letterAlpha = 1.0f; } //if multiline, show
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
lettersLeftLabel.alpha = letterAlpha;
lettersLeftLabel.textColor = letterColour;
}
completion:^(BOOL finished){
}];
//6. update submit colour based on minimum threshold
UIColor * submitColour = [_peacock.applePink colorWithAlphaComponent:0.5f];
bool enableSubmit = false;
if (numberOfCharacters >= minCharacters){
submitColour = _peacock.applePink;
enableSubmit = true;
}
[submitButton setEnabled:enableSubmit];
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[submitButton setTitleColor:submitColour forState:UIControlStateNormal];
}
completion:^(BOOL finished){
}];
//7. special case if you want to limit the frame size of the input TV to a specific number of lines
bool shouldEnableScroll = false;
int maxNumberOfLines = 5; //anything above this triggers the input TV to stay stationary and update its scroll
int actualNumberOfLines = textHeight / textView.font.lineHeight;
if (actualNumberOfLines >= maxNumberOfLines){ //recalculate vars for frames
textHeight = maxNumberOfLines * lineHeight;
additionalHeight = textHeight - lineHeight;
moveUpHeight = keyboardHeight + additionalHeight;
shouldEnableScroll = true;
}
//8. adjust frames of views
inputTV.frame = CGRectMake(5, 6, w-100, textHeight); //update immediately (parent view clips to bounds)
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
bottomView.frame = CGRectMake(0, h-45-moveUpHeight, w, 45+additionalHeight);
inputTVBG.frame = CGRectMake(10, 8, w-90, lineHeight+additionalHeight+13);
submitButton.frame = CGRectMake(w-70, additionalHeight, 60, 45);
}
completion:^(BOOL finished){
inputTV.scrollEnabled = shouldEnableScroll; //default disable scroll here to avoid bouncing
}];
}
In the above method, this is what's happening:
If you want to set a minimum or maximum number of characters, you can do so here. You pull the the number of characters and store as an integer, and calculate how many characters are left.
If the user has reached the maximum number of characters, reset the textView text by stripping back to your max.
These vars are used to calculate how much you need to move your bottomView up and also for the resizing its subviews.
This method is just to change the colour / text of some UI elements. It's not strictly necessary.
This method brings the lettersLeftLabel onto view if you're using that. It's not necessary either.
This enables the submit button only if the minimum number of characters has been reached. It changes the colour as an indicator to the user.
If you want to limit the growth of the inputTV and surrounding elements, you can include this bit of code. It requires you to set the maximum number of lines you want to show. If the user exceeds the max, scroll is reenabled for the inputTV, otherwise it defaults to false (important to stop it bouncing).
This is the main resizing logic, moving the bottomView up and resizing its child views. The submit button needs to stay in the same position, so move it down as the bottomView grows.
NOTE: If you just want the barebones code, you only need to implement steps 3 and 8.
Apple has some code samples that deal with this exact situation.
I finally got it working. Here is my solution, can you guys spot any errors in my design?
#synthesize textView = _textView;
#synthesize callbackViewController = _callbackViewController;
-(void)keyboardWasShown:(NSNotification*)aNotification {
if(keyboardShown) {
return;
}
NSDictionary *info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue *aValue = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];
keyboardSize = [aValue CGRectValue].size;
// Resize the scroll view (which is the root view of the window)
CGRect viewFrame = [self.textView frame];
orientationAtShown = orientation;
if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
viewFrame.size.height -= keyboardSize.height;
} else {
viewFrame.size.height -= keyboardSize.width;
}
self.textView.frame = viewFrame;
// Scroll the active text field into view.
//CGRect textFieldRect = [activeField frame];
[self.textView scrollRectToVisible:viewFrame animated:YES];
keyboardShown = YES;
}
-(void)keyboardWasHidden:(NSNotification*)aNotification {
if(!keyboardShown) {
return;
}
// Reset the height of the scroll view to its original value
CGRect viewFrame = [self.textView frame];
if(orientationAtShown == UIInterfaceOrientationPortrait || orientationAtShown == UIInterfaceOrientationPortraitUpsideDown) {
viewFrame.size.height += keyboardSize.height;
} else {
viewFrame.size.height += keyboardSize.width;
}
self.textView.frame = viewFrame;
keyboardShown = NO;
}
-(void)registerForKeyboardNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasHidden:)
name:UIKeyboardDidHideNotification object:nil];
}
-(void)viewWillAppear:(BOOL)animated {
keyboardShown = NO;
[self registerForKeyboardNotifications];
}
-(void)viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if(keyboardShown) {
[self keyboardWasHidden:nil];
}
orientation = interfaceOrientation;
CGRect viewFrame = [self.textView frame];
if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
if(viewFrame.size.width > viewFrame.size.height) {
CGRect viewFrameFixed = CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.height, viewFrame.size.width);
self.textView.frame = viewFrameFixed;
}
} else {
if(viewFrame.size.width < viewFrame.size.height) {
CGRect viewFrameFixed = CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.height, viewFrame.size.width);
self.textView.frame = viewFrameFixed;
}
}
// Return YES for supported orientations
return YES;
}
#Alejandro above has the right idea, but his code does not work in landscape mode. I have amended his keyboardWasShown: method to work correctly in all orientations:
- (void)keyboardWasShown:(NSNotification *)notification {
if (self.textView != nil) {
NSDictionary* info = [notification userInfo];
CGRect keyboardRect = [self.textView convertRect:[[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil];
CGSize keyboardSize = keyboardRect.size;
self.textView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
self.textView.scrollIndicatorInsets = self.textView.contentInset;
}
}
Add Observer first in viewDidLoad.
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
Call the methods
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.textView.contentInset = contentInsets;
self.textView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.textView.frame.origin) ) {
[self.textView scrollRectToVisible:self.textView.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.textView.contentInset = contentInsets;
self.textView.scrollIndicatorInsets = contentInsets;
}
if you have more then 1 textfield or you want to reduce your code then try this code
- (void)textFieldDidBeginEditing:(UITextField *)textField{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frame = self.view.frame;
frame.origin.y = (self.view.frame.size.height - textField.frame.origin.y) - self.view.frame.size.height+60;
if (frame.origin.y<-162) {
frame.origin.y = -162;
}
[self.view setFrame:frame];
[UIView commitAnimations];
}
-(BOOL)textFieldShouldEndEditing:(UITextField *)textField{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frame = self.view.frame;
frame.origin.y = 0;
[self.view setFrame:frame];
[UIView commitAnimations];
return YES;
}
Extending #alejandro & #Mani :
Th final answer:
- (void)keyboardWasShown:(NSNotification *)notification {
if (self.textView != nil) {
NSDictionary* info = [notification userInfo];
CGRect keyboardRect = [self.textNote convertRect:[[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil];
CGSize keyboardSize = keyboardRect.size;
self.textView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
self.textView.scrollIndicatorInsets = self.textView.contentInset;
}
}
- (void)keyboardWillBeHidden:(NSNotification*)notification {
self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
}