协慌网

登录 贡献 社区

当键盘存在时,如何让 UITextField 向上移动?

使用 iOS SDK:

我有一个带UITextFieldUIViewUITextField一个键盘。我需要它能够:

  1. 在键盘启动后,允许滚动UIScrollView的内容以查看其他文本字段

  2. 自动 “跳跃”(通过向上滚动)或缩短

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

我是否需要UIViewUIScrollView ?一个人进去另一个吗?

需要实现什么才能自动滚动到活动文本字段?

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

注意:我正在使用的UIView (或UIScrollView )是由 tabbar( 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
}

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

答案

  1. 如果您现在拥有的内容不适合 iPhone 屏幕,则只需要ScrollView 。 (如果要将ScrollView添加为组件的超级视图。只是为了在键盘出现时向上滚动TextField ,那么就不需要了。)

  2. 为了显示textfields而不被键盘隐藏,标准方法是在显示键盘时向上 / 向下移动具有文本字段的视图。

以下是一些示例代码:

#define kOFFSET_FOR_KEYBOARD 80.0

-(void)keyboardWillShow {
    // Animate the current view out of the way
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)keyboardWillHide {
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:mailTf])
    {
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [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 -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
    }
    else
    {
        // revert back to the normal state.
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

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

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

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

我在编写多个UITextFieldsUIScrollView遇到了很多问题,其中一个或多个UITextFields在编辑时会被键盘遮挡。

如果您的UIScrollView没有正确滚动,请考虑以下事项。

1)确保您的 contentSize 大于UIScrollView框架大小。理解UIScrollViews的方法是UIScrollView就像是在 contentSize 中定义的内容上的查看窗口。因此,为了使UIScrollview能够在任何地方滚动,contentSize 必须大于UIScrollView 。否则,不需要滚动,因为 contentSize 中定义的所有内容都已可见。 BTW,默认 contentSize = CGSizeZero

2)现在您已经了解UIScrollView实际上是 “内容” 的窗口,确保键盘不会遮挡UIScrollView's查看 “窗口” 的方法是调整UIScrollView大小,以便在键盘存在时,将UIScrollView窗口的大小设置为原始的UIScrollView frame.size.height 减去键盘的高度。这将确保您的窗口只是那个小的可视区域。

3)这里有一个问题:当我第一次实现这个时,我想我必须得到已编辑文本字段的CGRect并调用UIScrollView's scrollRecToVisible 方法。我通过调用scrollRecToVisible方法实现了UITextFieldDelegate方法textFieldDidBeginEditing 。这实际上有一个奇怪的副作用,滚动会将UITextField 捕捉到位。在最长的时间里,我无法弄清楚它是什么。然后我注释掉了textFieldDidBeginEditing Delegate 方法,它一切正常!!(???)。事实证明,我相信UIScrollView实际上隐式地将当前编辑的UITextField隐藏到可查看窗口中。我对UITextFieldDelegate方法的实现和随后对scrollRecToVisible调用是多余的,并且是导致奇怪副作用的原因。

因此,以下是在键盘出现时将UIScrollViewUITextField正确滚动到位的步骤。

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:self.view.window];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification 
                                               object:self.view.window];
    keyboardIsShown = NO;
    //make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
    CGSize scrollContentSize = CGSizeMake(320, 345);
    self.scrollView.contentSize = scrollContentSize;
}

- (void)keyboardWillHide:(NSNotification *)n
{
    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;


    // resize the scrollview
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];

    keyboardIsShown = NO;
}

- (void)keyboardWillShow:(NSNotification *)n
{
    // This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown.  This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`.  If we were to resize the `UIScrollView` again, it would be disastrous.  NOTE: The keyboard notification will fire even when the keyboard is already shown.
    if (keyboardIsShown) {
        return;
    }

    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;

    // resize the noteView
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];
    keyboardIsShown = YES;
}
  1. viewDidLoad注册键盘通知
  2. 取消注册viewDidUnload的键盘 nofitications
  3. 确保在viewDidLoad设置了contentSize并且大于UIScrollView
  4. 键盘存在时缩小 UIScrollView
  5. 键盘消失后恢复 UIScrollView
  6. 使用 ivar 检测键盘是否已经显示在屏幕上,因为每次UITextField都有标签时发送键盘通知,即使键盘已经存在,以避免在UIScrollView已经缩小缩小 UIScrollView

需要注意的一点是,当您在另一个UITextField上进行选项卡时,即使键盘已经在屏幕上, UIKeyboardWillShowNotification也会触发。我通过使用 ivar 来解决这个问题,以避免在键盘已经在屏幕上时调整UIScrollView大小。当键盘已经存在时无意中调整UIScrollView大小将是灾难性的!

希望这段代码可以让你们中的一些人头疼。

实际上,最好只使用 Apple 的实现,如文档中所提供的那样。但是,他们提供的代码是错误的。替换在keyboardWasShown:找到的部分keyboardWasShown:在注释的正下方以下内容:

NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);

CGPoint origin =  activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
    CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
    [backScrollView setContentOffset:scrollPoint animated:YES];
}

Apple 代码的问题是这些:(1)它们总是计算点是否在视图的框架内,但它是ScrollView ,因此它可能已经滚动,您需要考虑该偏移:

origin.y -= scrollView.contentOffset.y

(2)他们将 contentOffset 移动键盘的高度,但我们想要相反(我们希望将contentOffset移动到屏幕上可见的高度,而不是不是):

activeField.frame.origin.y-(aRect.size.height)