使用iOS SDK:

我有一个带UITextFields的UIView,可以启动键盘。我需要它能够:

启动键盘后,允许滚动UIScrollView的内容以查看其他文本字段自动“跳转”(通过向上滚动)或缩短

我知道我需要一个UIScrollView。我已经尝试将UIView的类更改为UIScrollView,但仍然无法上下滚动文本框。

我需要UIView和UIScrollView吗?一个在另一个里面吗?

要自动滚动到活动文本字段,需要执行哪些操作?

理想情况下,尽可能多的组件设置将在Interface Builder中完成。我只想编写需要的代码。

注意:我使用的UIView(或UIScrollView)是由一个选项卡(UITabBar)启动的,它需要正常工作。


我正在添加滚动条,只为键盘出现时使用。尽管不需要它,但我觉得它提供了一个更好的界面,例如,用户可以滚动和更改文本框。

当键盘上下移动时,我可以改变UIScrollView的框架大小。我只是在使用:

-(void)textFieldDidBeginEditing:(UITextField *)textField {
    //Keyboard becomes visible
    scrollView.frame = CGRectMake(scrollView.frame.origin.x,
                                  scrollView.frame.origin.y,
    scrollView.frame.size.width,
    scrollView.frame.size.height - 215 + 50);   // Resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
    // Keyboard will hide
    scrollView.frame = CGRectMake(scrollView.frame.origin.x,
                                  scrollView.frame.origin.y,
                                  scrollView.frame.size.width,
                                  scrollView.frame.size.height + 215 - 50); // Resize
}

然而,这不会自动“向上移动”或将可见区域中的下部文本字段居中,这是我真正想要的。


当前回答

这是我使用自动布局的版本:

其想法是将包含文本字段/文本视图的视图嵌入UIScrollView中,从底部到其超级视图设置一个约束,创建一个出口,并使用通知根据键盘高度更新其常量。这是基于此处的Apple示例,以及此处使用AutoLayout的UIScrollView上的Apple技术说明。

1) 将视图V嵌入UIScrollView S:如果已经设置了常量和子视图,则可以在ViewController的视图中复制/粘贴视图和子视图;然后使用编辑器->嵌入菜单嵌入视图,最后删除复制的视图。)

2) 设置以下约束:

S到顶部布局指南:0S至底部布局指南:0通向超级视图的S:0S尾随到超级视图:0V顶部空间到超级视图:0V底部空间到超级视图:0超级视图的V尾随空格:0超视图的V前导空格:0V等于S的宽度最新底部V子视图到超级视图:20

3) 从视图控制器的最新约束创建出口

4) 使用以下代码:

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomSpaceToContentView;

// ...

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // ...

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Handle keyboard

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    self.bottomSpaceToContentView.constant = kBottomMargin + kbSize.height;
    [self.view layoutIfNeeded];
}

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    self.bottomSpaceToContentView.constant = kBottomMargin;
    [self.view layoutIfNeeded];
}

哈哈,它起作用了!

其他回答

一个适用于许多UITextFields的小补丁:

#pragma mark UIKeyboard handling

#define kMin 150

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if (currTextField) {
        [currTextField release];
    }
    currTextField = [sender retain];
    // Move the main view, so that the keyboard does not hide it.
    if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
        [self setViewMovedUp:YES];
    }
}


// Method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3]; // If you want to slide up the view

    CGRect rect = self.view.frame;
    if (movedUp)
    {
        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard
        // 2. increase the size of the view so that the area behind the keyboard is covered up.
        rect.origin.y = kMin - currTextField.frame.origin.y ;
    }
    else
    {
        // Revert back to the normal state.
        rect.origin.y = 0;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


- (void)keyboardWillShow:(NSNotification *)notif
{
    // Keyboard will be shown now. Depending on which textfield is active, move up or move down the view appropriately

    if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
    {
        [self setViewMovedUp:YES];
    }
    else if (![currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y < kMin)
    {
        [self setViewMovedUp:NO];
    }
}


- (void)keyboardWillHide:(NSNotification *)notif
{
    // Keyboard will be shown now. Depending on which textfield is active, move up or move down the view appropriately
    if (self.view.frame.origin.y < 0 ) {
        [self setViewMovedUp:NO];
    }
}


- (void)viewWillAppear:(BOOL)animated
{
    // 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];
}

- (void)viewWillDisappear:(BOOL)animated
{
    // Unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}

我发现@DK_是我开始使用的解决方案。然而,假设scrollView覆盖了整个视图。我不是这样的。我只想要一个滚动视图,以防键盘覆盖了登录屏幕上的下文本字段。因此,我的内容视图与滚动视图大小相同,比主视图小。

它也没有考虑到景观,这是我开始遇到麻烦的地方。玩了几天后,这是我的键盘显示:方法。

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    // A lot of the inspiration for this code came from http://stackoverflow.com/a/4837510/594602
    CGFloat height = 0;
    NSDictionary* info = [aNotification userInfo];

    CGRect kbFrameRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect kbBoundsRect = [self.view convertRect:kbFrameRect fromView:nil]; // Convert frame from window to view coordinates.

    CGRect scrollRect = scrollView.frame;
    CGRect intersect = CGRectIntersection(kbBoundsRect, scrollRect);

    if (!CGRectIsNull(intersect))
    {
        height = intersect.size.height;
        UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, height, 0.0);
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    // Figure out what the view rectangle is for the scrollView
    CGPoint contentOffset = scrollView.contentOffset;
    CGRect visibleRect = CGRectOffset(scrollRect, contentOffset.x, contentOffset.y);    // I'm not 100% sure if this is needed/right. My scrollView was always at the top in testing.
    visibleRect.size.height -= height;
    CGRect activeRect = activeField.frame;

    if (!CGRectContainsRect(visibleRect, activeRect))
    {
        [self.scrollView scrollRectToVisible:activeField.frame animated:YES];
    }
}

我在使用自动布局时也遇到了一些困难。如果我没有正确完成布局,我就没有得到预期的滚动效果。有一件事让生活变得更加简单,那就是将所有要滚动的项目放在一个视图中,并将其作为滚动视图中的唯一项目。我把这个单一视图称为“内容视图”。

我认为关键部分是内容视图有一个设定的宽度和高度。这使得滚动视图能够准确地知道需要处理多少内容。这与通常的布局有点不同。通常情况下,视图会尽量占据更多空间。对于滚动视图的内容,您试图使视图尽可能地限制自己。内容视图允许您停止此操作。所以我给了我248的高度,并使用320的标准屏幕宽度作为我的宽度。

最终对我有用的布局如下:

滚动视图到超级视图:基本上我给了顶部、左侧和右侧的约束。水平空间-视图-滚动视图(0)垂直空间-视图-滚动视图(0)水平空间-滚动视图-视图(0)滚动视图高度:我将滚动视图设置为恒定高度。我不知道这是否真的有必要,但它有滚动视图本身的界限。高度-(248)-滚动视图滚动视图的内容视图:我给了所有方面的常量,顶部、左侧、底部和右侧。垂直空间-视图-滚动视图(0)垂直空间-滚动视图-视图(0)水平空间-视图-滚动视图(0)水平空间-滚动视图-视图(0)内容视图的维度。高度-(248)-视图宽度-(320)-视图

这是我使用自动布局的版本:

其想法是将包含文本字段/文本视图的视图嵌入UIScrollView中,从底部到其超级视图设置一个约束,创建一个出口,并使用通知根据键盘高度更新其常量。这是基于此处的Apple示例,以及此处使用AutoLayout的UIScrollView上的Apple技术说明。

1) 将视图V嵌入UIScrollView S:如果已经设置了常量和子视图,则可以在ViewController的视图中复制/粘贴视图和子视图;然后使用编辑器->嵌入菜单嵌入视图,最后删除复制的视图。)

2) 设置以下约束:

S到顶部布局指南:0S至底部布局指南:0通向超级视图的S:0S尾随到超级视图:0V顶部空间到超级视图:0V底部空间到超级视图:0超级视图的V尾随空格:0超视图的V前导空格:0V等于S的宽度最新底部V子视图到超级视图:20

3) 从视图控制器的最新约束创建出口

4) 使用以下代码:

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomSpaceToContentView;

// ...

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // ...

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Handle keyboard

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    self.bottomSpaceToContentView.constant = kBottomMargin + kbSize.height;
    [self.view layoutIfNeeded];
}

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    self.bottomSpaceToContentView.constant = kBottomMargin;
    [self.view layoutIfNeeded];
}

哈哈,它起作用了!

我认为如果您使用Swift,最好的方法是使用面向协议的编程。

首先,您必须创建KeyboardCapable协议,使符合该协议的任何UIViewController都能够注册和注销键盘观察员:

import Foundation
import UIKit

protocol KeyboardCapable: KeyboardAnimatable {
    func keyboardWillShow(notification: NSNotification)
    func keyboardWillHide(notification: NSNotification)
}

extension KeyboardCapable where Self: UIViewController {
    func registerKeyboardNotifications() {
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterKeyboardNotifications() {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }
}

您已经注意到上述代码中有一个无关的KeyboardAnimatable关键字。这只是我们需要创建的下一个协议的名称:

import Foundation
import UIKit

protocol KeyboardAnimatable {

}

extension KeyboardAnimatable where Self: UIViewController {
    func performKeyboardShowFullViewAnimation(withKeyboardHeight height: CGFloat, andDuration duration: NSTimeInterval) {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            self.view.frame = CGRectMake(view.frame.origin.x, -height, view.bounds.width, view.bounds.height)
            }, completion: nil)
    }

    func performKeyboardHideFullViewAnimation(withDuration duration: NSTimeInterval) {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            self.view.frame = CGRectMake(view.frame.origin.x, 0.0, view.bounds.width, view.bounds.height)
            }, completion: nil)
    }
}

这个KeyboardAnimatable协议为所有符合它的UIViewController提供了两种方法,分别为整个视图上下设置动画。

好的,如果KeyboardCapable符合KeyboardAnimatable,则所有符合Keyboard Capable的UIViewController也符合KeyboaardAnimatable。这很酷。

让我们看看符合KeyboardCapable的UIViewController,并对键盘事件做出反应:

import Foundation
import UIKit

class TransferConfirmViewController: UIViewController, KeyboardCapable {
    //MARK: - LIFE CYCLE       
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        registerKeyboardNotifications()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)

        unregisterKeyboardNotifications()
    }

    //MARK: - NOTIFICATIONS
    //MARK: Keyboard
    func keyboardWillShow(notification: NSNotification) {
        let keyboardHeight = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
        let animationDuration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        performKeyboardShowFullViewAnimation(withKeyboardHeight: keyboardHeight, andDuration: animationDuration)
    }

    func keyboardWillHide(notification: NSNotification) {
        let animationDuration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        performKeyboardHideFullViewAnimation(withDuration: animationDuration)
    }
}

现在,您的UIViewController将响应键盘事件,并将因此产生动画效果。

注意:如果您想要自定义动画而不是推拉视图,则必须在KeyboardAnimatable协议上定义自定义方法或在Keyboard功能上执行这些方法。这取决于你。

这对我有用:

func setupKeyboardNotifications() {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWasShown:"), name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillBeHidden:"), name: UIKeyboardWillHideNotification, object: nil)
}

func keyboardWasShown(aNotification:NSNotification) {
    let info = aNotification.userInfo
    let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as NSValue
    let kbSize = infoNSValue.CGRectValue().size
    UIView.beginAnimations(nil, context: nil)
    UIView.setAnimationDuration(0.3)
    var rect : CGRect = self.view.frame
    rect.size.height -= kbSize.height

    self.view.frame = rect
    UIView.commitAnimations()
}

func keyboardWillBeHidden(aNotification:NSNotification) {
    let info = aNotification.userInfo
    let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as NSValue
    let kbSize = infoNSValue.CGRectValue().size
    UIView.beginAnimations(nil, context: nil)
    UIView.setAnimationDuration(0.3)
    var rect : CGRect = self.view.frame
    rect.size.height += kbSize.height
    self.view.frame = rect
    UIView.commitAnimations()
}