I know that this question has been asked over and over again, but nothing seems to be working for me. Most of the solutions around are pretty out of date, and the rest are incre
I spent sometime on this problem and gathered pieces code to create one final solution. My problem was related to UITableView scrolling and keyboard open/close.
You need two partial methods in your cell class:
void EditingBegin(UITextField sender)
{
// Height of tallest cell, you can ignore this!
float tableMargin = 70.0f;
float tableHeight = _tableView.Frame.Size.Height;
float keyBoardHeight = KeyboardHeight();
NSIndexPath[] paths = this._tableView.IndexPathsForVisibleRows;
RectangleF rectLast = this._tableView.RectForSection(paths[paths.Length - 1].Section);
RectangleF rectFirst = this._tableView.RectForSection(paths[0].Section);
float lastCellY = rectLast.Y - rectFirst.Y;
if (lastCellY > tableHeight - keyBoardHeight)
{
float diff = lastCellY - (tableHeight - tableMargin - keyBoardHeight);
this._tableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, diff, 0.0f);
}
float cellPosition = this._tableView.RectForSection(this._section).Y;
if (cellPosition > tableHeight - keyBoardHeight)
{
if (this._tableView.ContentInset.Bottom == 0.0f)
{
float diff = cellPosition - (tableHeight - tableMargin - keyBoardHeight);
this._tableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, diff, 0.0f);
}
else
{
this._tableView.ScrollToRow(NSIndexPath.FromItemSection(0, this._section), UITableViewScrollPosition.Middle, true);
}
}
}
partial void EditingEnd(UITextField sender)
{
UIView.BeginAnimations(null);
UIView.SetAnimationDuration(0.3f);
this._tableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, 0.0f, 0.0f);
UIView.CommitAnimations();
}
and then in your view controller class:
public override void WillAnimateRotation(UIInterfaceOrientation toInterfaceOrientation, double duration)
{
base.WillAnimateRotation(toInterfaceOrientation, duration);
float bottom = this.TableView.ContentInset.Bottom;
if (bottom > 0.0f)
{
if (toInterfaceOrientation == UIInterfaceOrientation.Portrait || toInterfaceOrientation == UIInterfaceOrientation.PortraitUpsideDown)
{
bottom = bottom * UIScreen.MainScreen.Bounds.Width / UIScreen.MainScreen.Bounds.Height;
}
else
{
bottom = bottom * UIScreen.MainScreen.Bounds.Height / UIScreen.MainScreen.Bounds.Width;
}
UIEdgeInsets insets = this.TableView.ContentInset;
this.TableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, bottom, 0.0f);
}
}
If you have a UITableView
or a UIScrollView
it's better to change values for contentOffset
instead of making changes to the frame
.
Working on Peter's Answer, adding this method to your class works nicely:
- (void)textViewDidBeginEditing:(UITextField *)textField {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGPoint offset = self.tableView.contentOffset;
offset.y += 200; // You can change this, but 200 doesn't create any problems
[self.tableView setContentOffset:offset];
[UIView commitAnimations];
}
That's it, no need to add the textViewDidEndEditing
method.
I shouldn't need to say this, but for this to work your UITextField
or UITextView
must be a delegate of your controller.
Since I found it, I use TPKeyboardAvoiding - https://github.com/michaeltyson/TPKeyboardAvoiding.
It is working great, and is very easy to setup:
UIScrollView
into your view controller's xibTPKeyboardAvoidingScrollView
(still in the xib, via the identity inspector)You can also create it programmatically, if you want.
There is a class for the same need inside a UITableViewController
; it is only needed in case you support a version of iOS below 4.3.
Starting with Peter's answer, I developed the following approach in Swift 3.0 under iOS 10.1. I'm doing this for a textView, so I have implemented the UITextViewDelegate functions textViewDidBeginEditing and textViewDidEndEditing where I adjust the view's bounds. As you can see, I set the origin Y value to a small positive number to scroll up and then back to 0 to return to the original position.
Here is the relevant code from my ViewController. You don't need to animate, but it adds a nice touch.
func textViewDidBeginEditing(_ textView: UITextView)
{
if UIScreen.main.bounds.height < 568 {
UIView.animate(withDuration: 0.75, animations: {
self.view.bounds.origin.y = 60
})
}
}
func textViewDidEndEditing(_ textView: UITextView)
{
if UIScreen.main.bounds.height < 568 {
UIView.animate(withDuration: 0.75, animations: {
self.view.bounds.origin.y = 0
})
}
}
i have a scrollview and 3 text fields in this. I have a simple code from my own application :
.h file is :
#import <UIKit/UIKit.h>
@interface AddContactViewController : UIViewController<UITextFieldDelegate, UIScrollViewDelegate>
@property (nonatomic, retain) NSDictionary *dict_contactDetail;
@property (nonatomic, retain) IBOutlet UILabel *lbl_name;
@property (nonatomic, retain) IBOutlet UITextField *txtField_tel;
@property (nonatomic, retain) IBOutlet UITextField *txtField_address;
@property (nonatomic, retain) IBOutlet UITextField *txtField_email;
@property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
@end
.m file :
#import "AddContactViewController.h"
@interface AddContactViewController ()
@end
@implementation AddContactViewController
@synthesize dict_contactDetail;
@synthesize lbl_name, txtField_tel, txtField_email, txtField_address, scrollView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// NSLog(@"dict_contactDetail : %@", dict_contactDetail);
UIBarButtonItem * rightButton = [[UIBarButtonItem alloc] initWithTitle:@"Add" style:UIBarButtonSystemItemDone target:self action:@selector(addEmergencyContact:)];
self.navigationItem.rightBarButtonItem = rightButton;
lbl_name.text = [NSString stringWithFormat:@"%@ %@", [dict_contactDetail valueForKey:@"fname"], [dict_contactDetail valueForKey:@"lname"]];
txtField_tel.returnKeyType = UIReturnKeyDone;
txtField_email.returnKeyType = UIReturnKeyDone;
txtField_address.returnKeyType = UIReturnKeyDone;
}
-(void)addEmergencyContact:(id)sender
{
scrollView.frame = CGRectMake(0, 0, 320, 460);
}
#pragma mark - text field delegates
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if([textField isEqual:txtField_tel])
{
[scrollView setContentOffset:CGPointMake(0, 70)];
scrollView.frame = CGRectMake(0, 0, 320, 210);
}
if([textField isEqual:txtField_address])
{
[scrollView setContentOffset:CGPointMake(0, 140)];
scrollView.frame = CGRectMake(0, 0, 320, 210);
}
if([textField isEqual:txtField_email])
{
[scrollView setContentOffset:CGPointMake(0, 210)];
scrollView.frame = CGRectMake(0, 0, 320, 210);
}
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
scrollView.frame = CGRectMake(0, 0, 320, 460);
[textField resignFirstResponder];
return YES;
}
@end
@BenLu and other users who are facing problem of the function are never getting called is because of following reason: As the delegate inbuild function bydefaults return void instead of BOOL this is how it should be as follows:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frame = self.view.frame;
frame.origin.y = -100;
[self.view setFrame:frame];
[UIView commitAnimations];
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frame = self.view.frame;
frame.origin.y = 100;
[self.view setFrame:frame];
[UIView commitAnimations];
}