当键盘出现时如何将视图向上滚动?

38

我知道这个问题已经被反复提出,但对我来说似乎没有什么解决办法。大部分的解决方案都已经过时了,剩下的则是巨大的代码块,比实际项目编码的大十倍。我有几个垂直排列的UITextFields,但当键盘启动以编辑其中一个文本字段时,它会覆盖文本字段。我想知道是否有一种简单的初学者方法来滚动视图向上,然后在开始和结束编辑时再滚回去?

谢谢。


可能是 如何在键盘出现时使UITextField向上移动? 的重复问题。 - David Z
7个回答

29

我曾经使用键盘通知和当前第一响应者的检测来处理可滚动及不可滚动视图的问题,但有时我会使用以下简单解决方案:通过文本字段代理的textViewDidBeginEditing:方法检测键盘的打开,并将整个视图向上移动。最简单的方法是类似于将self.view.bounds.origin.y更改为-100(或其他值)。使用相应的textViewShouldEndEditing:方法将其设置为相反的值,在这种情况下是100。更改bounds是一个相对过程。更改后,框架被移动,但边界原点仍然为零。


6
谢谢。这是我到目前为止的代码:`-(BOOL)textViewDidBeginEditing:(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函数的结尾”。我不知道该怎么解决,很抱歉我还是个初学者。 - Henry F
1
在方法末尾添加return YES;以表示您允许编辑。不要忘记将此类设置为该文本视图的委托。如果需要,UITextFieldDelegates还有一个相应的方法称为textFieldDidBeginEditing: - Peter DeWeese
太棒了,这可解决了问题,非常感谢。不过我还有一个问题,如何将这个类设置为那个文本视图的代理呢?如果这是个愚蠢的问题,真的很抱歉 lol。 - Henry F
你可以通过在xib/storyboard中右击文本视图然后从委托拖动到文件所有者来完成它,或者你可以在viewDidLoad中以编程方式在该类中实例化文本视图后完成它,例如 myTextView.delegate = self - Peter DeWeese
我遇到了和你们一样的问题,并且按照你们所做的那样去尝试。然而,-textViewDidBeginEditing-textFieldShouldEndEditing 从未被调用。为什么? - Ben Lu
另外,当您隐藏键盘时,可以执行以下操作。-(BOOL)textFieldShouldEndEditing:(UITextField *)textField { [UIView setAnimationDuration:0.35f]; CGRect frame = self.view.frame; frame.origin.y = 0; [self.view setFrame:frame]; [UIView commitAnimations];} - Erhan Demirci

25
自从我找到了它,我就一直在使用 TPKeyboardAvoiding - https://github.com/michaeltyson/TPKeyboardAvoiding
它的效果非常好,而且非常容易设置:
- 在您的视图控制器的 xib 中添加一个 UIScrollView。 - 将滚动视图的类设置为 TPKeyboardAvoidingScrollView(仍然在 xib 中,通过身份检查器)。 - 将所有控件放置在该滚动视图中。
如果您愿意,还可以通过编程方式创建它。
同样,在UITableViewController中也有一个满足相同需求的类;只有在支持低于4.3版本的iOS时才需要。

很酷,谢谢,我现在要尝试一下。这可能是一个愚蠢的问题,但我已经设置了nib,如果在nib上面添加一个滚动视图会有什么影响吗?(我还在ImageView中设置了背景) - Henry F
只需将视图添加到nib中,然后将其他元素移动到视图内部。所有元素的属性(IBOutlet、配置)都将保留。如果你出现了问题,则可以取消上一步骤(或回退到最新提交的版本!)。 - Guillaume
如果这个库是由迈克尔·泰森(Micheal Tyson)编写的,那我就使用它。很简单。 - abbood
如果你和我一样,有自定义的输入视图和输入附件视图,那么这是最简单的解决方案。 - michaelsnowden
这个库现在可以正常工作了,如果我的最后一个文本视图正在增长,那么我的文本视图会下降到键盘的一侧。 - Ganesh Pawar

3
@BenLu和其他遇到函数从未被调用问题的用户,是因为以下原因: 由于委托内置函数默认返回void而不是BOOL,所以应该按照以下方式进行更改:
 -(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];
}

1
我是一位能翻译文本的助手。
我花了一些时间,收集了一些代码片段,创造了一个最终解决方案。我的问题与UITableView滚动和键盘的打开/关闭有关。
您需要在您的单元格类中使用两个部分方法:
    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();
    }

然后在您的视图控制器类中:

    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);
        }
    }  

0

从Peter的答案开始,我在iOS 10.1下使用Swift 3.0开发了以下方法。我正在为textView执行此操作,因此我已经实现了UITextViewDelegate函数textViewDidBeginEditing和textViewDidEndEditing,在其中调整视图的边界。正如您所看到的,我将原点Y值设置为一个小正数以向上滚动,然后返回到0以返回到原始位置。

这是来自我的ViewController的相关代码。您不需要进行动画,但它会增加一些美感。

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
        })
    }
}

0
如果你有一个 `UITableView` 或者一个 `UIScrollView`,最好使用 `contentOffset` 来改变值,而不是对 `frame` 进行更改。
Peter's Answer 上工作时,将这个方法添加到你的类中效果很好:
- (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];
}

就这样,不需要添加textViewDidEndEditing方法。


我不应该说这个,但是为了使它工作,你的UITextFieldUITextView必须是控制器的代理

-1

我有一个ScrollView和3个文本字段。我有一个来自我的应用程序的简单代码:

.h文件如下:

#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 文件:

#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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接