How to scroll view up when keyboard appears?

2019-03-08 13:56发布

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 incredibly huge blocks of code that are ten times larger then the actual projects coding. I have a few UITextFields lined up vertically, but when the keyboard launches to edit one, it covers up the text field. I was wondering if there is a simple beginner way to scroll the view up, and then back down when the editing starts and ends?

Thank you.

7条回答
萌系小妹纸
2楼-- · 2019-03-08 14:05

Since I found it, I use TPKeyboardAvoiding - https://github.com/michaeltyson/TPKeyboardAvoiding.

It is working great, and is very easy to setup:

  • Add a UIScrollView into your view controller's xib
  • Set the scroll view's class to TPKeyboardAvoidingScrollView (still in the xib, via the identity inspector)
  • Place all your controls within that scroll view

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.

查看更多
聊天终结者
3楼-- · 2019-03-08 14:06

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);
        }
    }  
查看更多
对你真心纯属浪费
4楼-- · 2019-03-08 14:06

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.

查看更多
Summer. ? 凉城
5楼-- · 2019-03-08 14:10

@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];
}
查看更多
手持菜刀,她持情操
6楼-- · 2019-03-08 14:12

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
查看更多
做自己的国王
7楼-- · 2019-03-08 14:22

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
        })
    }
}
查看更多
登录 后发表回答