当键盘出现时 - 如何开始编辑时,如何让UITextField向上移动?

问题描述 投票:1637回答:91

使用iOS SDK:

我有一个UIViewUITextFields,带来一个键盘。我需要它能够:

  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
}

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

ios objective-c uiscrollview uitextfield uikeyboard
91个回答
1019
投票
  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];
}

62
投票

Shiun说:“事实证明,我认为UIScrollView实际上隐含地将当前编辑的UITextField隐含地带入可视窗口”这似乎适用于iOS 3.1.3,但不适用于3.2,4.0或4.1。我必须添加一个显式的scrollRectToVisible,以使UITextField在iOS> = 3.2上可见。


47
投票

需要考虑的一件事是你是否想要自己使用// Called when UIKeyboardWillShowNotification is sent - (void)keyboardWillShow:(NSNotification*)notification { // if we have no view or are not visible in any window, we don't care if (!self.isViewLoaded || !self.view.window) { return; } NSDictionary *userInfo = [notification userInfo]; CGRect keyboardFrameInWindow; [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow]; // the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil]; CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView); UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0); // this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary. [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]]; [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; _scrollView.contentInset = newContentInsets; _scrollView.scrollIndicatorInsets = newContentInsets; /* * Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element * that should be visible, e.g. a purchase button below an amount text field * it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields */ if (_focusedControl) { CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view. CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y; CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height; // this is the visible part of the scroll view that is not hidden by the keyboard CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height; if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question // scroll up until the control is in place CGPoint newContentOffset = _scrollView.contentOffset; newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight); // make sure we don't set an impossible offset caused by the "nice visual offset" // if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight); [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code } else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) { // if the control is not fully visible, make it so (useful if the user taps on a partially visible input field CGPoint newContentOffset = _scrollView.contentOffset; newContentOffset.y = controlFrameInScrollView.origin.y; [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code } } [UIView commitAnimations]; } // Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillHide:(NSNotification*)notification { // if we have no view or are not visible in any window, we don't care if (!self.isViewLoaded || !self.view.window) { return; } NSDictionary *userInfo = notification.userInfo; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]]; [UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; // undo all that keyboardWillShow-magic // the scroll view will adjust its contentOffset apropriately _scrollView.contentInset = UIEdgeInsetsZero; _scrollView.scrollIndicatorInsets = UIEdgeInsetsZero; [UIView commitAnimations]; } 。我没有遇到任何精心设计的iPhone应用程序,实际上在UITextField之外使用UITextFields

这将是一些额外的工作,但我建议您实现表视图的所有数据输入视图。添加UITableViewCells到你的UITextView


46
投票

UITableViewCells文档详细介绍了此问题的解决方案。查看“移动位于键盘下的内容”下的源代码。这很简单。

编辑:注意到示例中有一个小问题。你可能想听听This而不是UIKeyboardWillHideNotification。否则,键盘关闭动画的持续时间将剪切键盘后面的滚动视图。


32
投票

找到最简单的解决方案

UIKeyboardDidHideNotification

31
投票

适用于许多UITextField的小修复

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}


- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = 80; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

30
投票

RPDP的代码成功地将文本字段移出键盘。但是当您在使用和解除键盘后滚动到顶部时,顶部已向上滚动到视图之外。这对于模拟器和设备来说都是如此。要阅读该视图顶部的内容,必须重新加载视图。

是不是他的下面的代码应该让视图回落?

#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 for 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 for 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]; 
}

23
投票

我不确定移动视图是否是正确的方法,我以不同的方式做了,调整UIScrollView的大小。我在一个小的else { // revert back to the normal state. rect.origin.y += kOFFSET_FOR_KEYBOARD; rect.size.height -= kOFFSET_FOR_KEYBOARD; } 上详细解释了它


22
投票

要恢复原始视图状态,请添加:

article

20
投票

尝试这个简短的技巧。

-(void)textFieldDidEndEditing:(UITextField *)sender

{
    //move the main view, so that the keyboard does not hide it.
    if  (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

19
投票

有这么多解决方案,但我花了几个小时才开始工作。所以,我把这段代码放在这里(只需粘贴到项目中,任何修改都不需要):

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

P.S:我希望代码可以帮助人们快速达到预期的效果。 (Xcode 4.5)


445
投票

我在UIScrollView组成多个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方法。我通过调用UITextFieldDelegate方法实现了textFieldDidBeginEditing方法scrollRecToVisible。这实际上有一个奇怪的副作用,滚动将UITextField卡入位置。在最长的时间里,我无法弄清楚它是什么。然后我评论了textFieldDidBeginEditing委托方法,这一切都有效!!(???)。事实证明,我相信UIScrollView实际上隐含地将当前编辑的UITextField隐含地带入可查看窗口。我对UITextFieldDelegate方法的实现以及随后对scrollRecToVisible的调用是多余的,并且是导致奇怪副作用的原因。

所以这是在键盘出现时将UITextField中的UIScrollView正确滚动到位的步骤。

// 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. 在qazxsw poi取消注册键盘通知
  3. 确保在viewDidUnload设置contentSize并且大于你的UIScrollView
  4. 键盘存在时缩小viewDidLoad
  5. 当键盘消失时,恢复UIScrollView
  6. 使用ivar来检测键盘是否已经显示在屏幕上,因为每次UIScrollView被标签时都会发送键盘通知,即使键盘已经存在,以避免在UITextField已经缩小时缩小UIScrollView

有一点需要注意的是,当你在另一个UIKeyboardWillShowNotification上选项卡时,即使键盘已经在屏幕上,UITextField也会触发。当键盘已经在屏幕上时,我使用ivar来避免重新调整UIScrollView的大小。当键盘已经存在时,无意中调整了UIScrollView的大小将是灾难性的!

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


18
投票

@ user271753

要将您的视图恢复为原始添加:

@interface RegistrationViewController : UIViewController <UITextFieldDelegate>{
    UITextField* activeField;
    UIScrollView *scrollView;
}
@end

- (void)viewDidLoad
{
    [super viewDidLoad];

    scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];

    //scrool view must be under main view - swap it
    UIView* natView = self.view;
    [self setView:scrollView];
    [self.view addSubview:natView];

    CGSize scrollViewContentSize = self.view.frame.size;
    [scrollView setContentSize:scrollViewContentSize];

    [self registerForKeyboardNotifications];
}

- (void)viewDidUnload {
    activeField = nil;
    scrollView = nil;
    [self unregisterForKeyboardNotifications];
    [super viewDidUnload];
}

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShown:)
                                                 name:UIKeyboardWillShowNotification object:nil];

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

}

-(void)unregisterForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillShowNotification
                                                  object:nil];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillHideNotification
                                                  object:nil];
}

- (void)keyboardWillShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    CGRect frame = self.view.frame;
    frame.size.height -= kbSize.height;
    CGPoint fOrigin = activeField.frame.origin;
    fOrigin.y -= scrollView.contentOffset.y;
    fOrigin.y += activeField.frame.size.height;
    if (!CGRectContainsPoint(frame, fOrigin) ) {
        CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height);
        [scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
     [scrollView setContentOffset:CGPointZero animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    activeField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeField = nil;
}

-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

16
投票

它不需要滚动视图就可以移动视图框。您可以更改-(BOOL)textFieldShouldReturn:(UITextField *)textField{ [textField resignFirstResponder]; [self setViewMovedUp:NO]; return YES; } 视图的框架,以便整个视图向上移动到足以将第一个响应文本字段放在键盘上方。当我遇到这个问题时,我创建了一个viewcontroller's的子类来执行此操作。它观察到键盘将出现通知并找到第一响应者子视图并且(如果需要)它向上动画主视图足够使得第一响应者在键盘上方。当键盘隐藏时,它会将视图设置回原位。

要使用此子类,请将自定义视图控制器作为UIViewController的子类,并继承此功能(只需确保实现GMKeyboardVCviewWillAppear,它们必须调用super)。该课程在viewWillDisappear


14
投票

斯威夫特4。

你可以轻松地向上和向下移动githubUITextFieldUIViewUIKeyBoard Animation

enter image description here

12
投票

这是我为特定布局提出的黑客解决方案。该解决方案类似于Matt Gallagher解决方案,即将一个部分滚动到视图中。我还是iPhone开发的新手,我不熟悉布局的工作方式。因此,这个黑客。

我的实现需要支持在单击字段时滚动,并在用户选择键盘上的下一个时滚动。

我有一个高度为775的UIView。控件基本上以3个为一组在大空间内展开。我最终得到了以下IB布局。

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var textField: UITextField!
    @IBOutlet var chatView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.resignFirstResponder()
    }

    @objc func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y
        print("deltaY",deltaY)

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.chatView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
        },completion: nil)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }

}

黑客来了

我将UIScrollView高度设置为比实际布局大12个单位(1250)。然后我创建了一个数组,其中包含我需要滚动到的绝对位置,以及一个基于IB标签号来获取它们的简单函数。

UIView -> UIScrollView -> [UI Components]

现在您需要做的就是在textFieldDidBeginEditing和textFieldShouldReturn中使用以下两行代码(如果您要创建下一个字段导航,则使用后一行代码)

static NSInteger stepRange[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};

NSInteger getScrollPos(NSInteger i) {
    if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
        return 0 ;
    return stepRange[i] ;
}

一个例子。

CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;

此方法不像其他方法那样“向后滚动”。这不是必要条件。再次,这是一个相当'高'的UIView,我没有几天学习内部布局引擎。


12
投票

根据- (void) textFieldDidBeginEditing:(UITextField *)textField { CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ; [self.scrollView setContentOffset:point animated:YES] ; } - (BOOL)textFieldShouldReturn:(UITextField *)textField { NSInteger nextTag = textField.tag + 1; UIResponder* nextResponder = [textField.superview viewWithTag:nextTag]; if (nextResponder) { [nextResponder becomeFirstResponder]; CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ; [self.scrollView setContentOffset:point animated:YES] ; } else{ [textField resignFirstResponder]; } return YES ; } ,从iOS 3.0开始,当存在文本字段的内嵌编辑时,the docs类会自动调整其表格视图的大小并重新定位。我认为将文本字段放在UITableViewController中是不够的,正如一些人所指出的那样。

来自UITableViewCell

表视图控制器支持对表视图行进行内联编辑;例如,如果行在编辑模式下嵌入了文本字段,则会将正在编辑的行滚动到显示的虚拟键盘上方。


11
投票

在这里,我找到了处理键盘的最简单的解决方案。

您只需复制粘贴下面的示例代码并更改文本字段或任何您想要移动的视图。

步骤1

只需在控制器中复制粘贴两个方法即可

the docs

第2步

分别在viewWillAppear和viewWillDisappear方法中注册和取消注册键盘通知。

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];
}

- (void)deregisterFromKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

步骤3

灵魂部分来了,只需替换你的文本域,并改变你想要向上移动的高度。

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self registerForKeyboardNotifications];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [self deregisterFromKeyboardNotifications];
    [super viewWillDisappear:animated];
}

参考:好吧,- (void)keyboardWasShown:(NSNotification *)notification { NSDictionary* info = [notification userInfo]; CGSize currentKeyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; //you need replace your textfield instance here CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin; CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height; CGRect visibleRect = self.view.frame; visibleRect.size.height -= currentKeyboardSize.height; if (!CGRectContainsPoint(visibleRect, textFieldOrigin)) { //you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height [self.scrollView setContentOffset:scrollPoint animated:YES]; } } - (void)keyboardWillBeHidden:(NSNotification *)notification { [self.scrollView setContentOffset:CGPointZero animated:YES]; } ,谁分享了这个美丽的代码剪辑,干净的解决方案。

希望这对那里的某人非常有帮助。


10
投票

一直在寻找关于这个主题的初学者的好教程,找到了最好的教程Please appreciate this guy

在本教程底部的here示例中,请务必留出空格

MIScrollView.h

正如你看到的。


10
投票

@property (nonatomic, retain) id backgroundTapDelegate; UITextField中时,应自动设置滚动。

如果不是,可能是因为tableview的代码/设置不正确。

例如,当我重新加载我的长桌,底部有一个UITableViewCell,如下所示,

UITextField

然后我在底部的文本字段被键盘遮挡了,当我在文本区域内单击时出现了键盘。

为了解决这个问题,我必须这样做 -

-(void) viewWillAppear:(BOOL)animated
{
   [self.tableview reloadData];
}

9
投票

使用此第三方,您甚至不需要写一行

-(void) viewWillAppear:(BOOL)animated { //add the following line to fix issue [super viewWillAppear:animated]; [self.tableview reloadData]; }

下载项目并在项目中拖放IQKeyboardManager。如果您发现任何问题,请阅读README文档。

真的是它消除了管理键盘的麻烦..

感谢和好运!


8
投票

注意:此答案假定您的textField位于scrollView中。

我更喜欢使用scrollContentInset和scrollContentOffset来处理它,而不是弄乱我的视图的帧。

首先让我们听一下键盘通知

https://github.com/hackiftekhar/IQKeyboardManager

下一步是保留一个表示当前第一响应者的属性(当前具有键盘的UITextfield / UITextVIew)。

我们使用委托方法来设置此属性。如果您正在使用其他组件,则需要类似的东西。

请注意,对于textfield,我们在didBeginEditing中设置它,在shouldBeginEditing中设置textView。这是因为textViewDidBeginEditing由于某种原因在UIKeyboardWillShowNotification之后被调用。

//call this from viewWillAppear
-(void)addKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}
//call this from viewWillDisappear
-(void)removeKeyboardNotifications{
    [[NSNotificationCenter default
    Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

最后,这是魔术

-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{
    self.currentFirstResponder = textView;
    return YES;
}

-(void)textFieldDidBeginEditing:(UITextField *)textField{
    self.currentFirstResponder = textField;
}

269
投票

实际上,最好只使用Apple的实现,如docs中所提供的那样。但是,他们提供的代码是错误的。将评论下面的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)

8
投票

这是使用Swift的解决方案。

- (void)keyboardWillShow:(NSNotification*)aNotification{
    NSDictionary* info = [aNotification userInfo];
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];


    /*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/
    if(self.currentFirstResponder){

        //keyboard origin in currentFirstResponderFrame
        CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil];

        float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y);

        //only scroll the scrollview if keyboard overlays the first responder
        if(spaceBetweenFirstResponderAndKeyboard>0){
            //if i call setContentOffset:animate:YES it behaves differently, not sure why
            [UIView animateWithDuration:0.25 animations:^{
                [self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)];
            }];
        }
    }

    //set bottom inset to the keyboard height so you can still scroll the whole content

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0);
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;

}

- (void)keyboardWillHide:(NSNotification*)aNotification{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;
}

244
投票

textFieldDidBeginEdittingtextFieldDidEndEditing中调用函数[self animateTextField:textField up:YES]如下:

-(void)textFieldDidBeginEditing:(UITextField *)textField 
{ 
    [self animateTextField:textField up:YES]; 
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField:textField up:NO];
}

-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
    const int movementDistance = -130; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? movementDistance : -movementDistance); 

    [UIView beginAnimations: @"animateTextField" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

我希望这段代码可以帮到你。

在Swift 2中

func animateTextField(textField: UITextField, up: Bool) 
{
     let movementDistance:CGFloat = -130
     let movementDuration: Double = 0.3

     var movement:CGFloat = 0
     if up 
     {
         movement = movementDistance
     }
     else 
     {
         movement = -movementDistance
     }
     UIView.beginAnimations("animateTextField", context: nil)
     UIView.setAnimationBeginsFromCurrentState(true)
     UIView.setAnimationDuration(movementDuration)
     self.view.frame = CGRectOffset(self.view.frame, 0, movement)
     UIView.commitAnimations()
}


func textFieldDidBeginEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:true)
}

func textFieldDidEndEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:false)
}

SWIFT 3

 func animateTextField(textField: UITextField, up: Bool)
    {
        let movementDistance:CGFloat = -130
        let movementDuration: Double = 0.3

        var movement:CGFloat = 0
        if up
        {
            movement = movementDistance
        }
        else
        {
            movement = -movementDistance
        }
        UIView.beginAnimations("animateTextField", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration)
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }


    func textFieldDidBeginEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:true)
    }

    func textFieldDidEndEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:false)
    }

135
投票

只需使用TextFields:

1a)使用Interface Builder:选择所有TextFields =>编辑=>嵌入=> ScrollView

1b)在UIScrollView中手动嵌入名为scrollView的TextFields

2)设置UITextFieldDelegate

3)设置每个textField.delegate = self;(或在Interface Builder建立连接)

4)复制/粘贴:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
    [scrollView setContentOffset:scrollPoint animated:YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    [scrollView setContentOffset:CGPointZero animated:YES];
}

113
投票

对于Universal Solution,这是我实现IQKeyboardManager的方法。

第1步: - 我在单例类中添加了UITextFieldUITextViewUIKeyboard的全局通知。我称之为IQKeyboardManager

第2步: - 如果发现UIKeyboardWillShowNotificationUITextFieldTextDidBeginEditingNotificationUITextViewTextDidBeginEditingNotification通知,我尝试从topMostViewController层次结构中获取UIWindow.rootViewController实例。为了正确发现UITextField / UITextView,需要调整topMostViewController.view的框架。

第3步: - 我计算了topMostViewController.view相对于第一个响应的UITextField / UITextView的预期移动距离。

Step4: - 我根据预期的移动距离向上/向下移动topMostViewController.view.frame

第5步: - 如果发现UIKeyboardWillHideNotificationUITextFieldTextDidEndEditingNotificationUITextViewTextDidEndEditingNotification通知,我再次尝试从topMostViewController层次结构中获取UIWindow.rootViewController实例。

步骤6: - 我计算了需要恢复到原始位置的topMostViewController.view的干扰距离。

Step7: - 我根据不安的距离恢复了topMostViewController.view.frame

Step8: - 我在app load上实例化了singleton IQKeyboardManager类实例,因此app中的每个UITextField / UITextView都会根据预期的移动距离自动调整。

这就是所有IQKeyboardManager为你做的没有任何代码行!只需要将相关的源文件拖放到项目中。 IQKeyboardManager还支持设备定位,自动UIToolbar管理,KeybkeyboardDistanceFromTextField以及比您想象的更多。


101
投票

我已经整理了一个通用的,插入式UIScrollViewUITableView甚至UICollectionView子类,它负责将其中的所有文本字段移出键盘。

当键盘即将出现时,子类将找到即将被编辑的子视图,并调整其帧和内容偏移以确保视图可见,并使用与键盘弹出窗口匹配的动画。当键盘消失时,它会恢复其先前的大小。

它应该基本上适用于任何设置,基于UITableView的界面,或者由手动放置的视图组成的界面。

她的手:演员


88
投票

对于Swift程序员:

这将为您完成所有工作,只需将它们放在视图控制器类中,并将solution for moving text fields out of the way of the keyboard实现到视图控制器并将textField的委托设置为UITextFieldDelegate

self

实现委托回调方法:

textField.delegate = self // Setting delegate of your UITextField to self

对于Swift 4,4.2,5:变化

func textFieldDidBeginEditing(textField: UITextField) {
    animateViewMoving(true, moveValue: 100)
}

func textFieldDidEndEditing(textField: UITextField) {
    animateViewMoving(false, moveValue: 100)
}

// Lifting the view up
func animateViewMoving (up:Bool, moveValue :CGFloat){
    let movementDuration:NSTimeInterval = 0.3
    let movement:CGFloat = ( up ? -moveValue : moveValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration )
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}

self.view.frame = CGRectOffset(self.view.frame, 0,  movement)

关于此实现的最后注意事项:如果在显示键盘时将另一个视图控制器推入堆栈,则会产生错误,其中视图返回到其中心框架但未重置键盘偏移。例如,您的键盘是nameField的第一个响应者,但是您按下了一个按钮,将您的帮助视图控制器推送到您的堆栈。要修复偏移错误,请确保在离开视图控制器之前调用nameField.resignFirstResponder(),以确保也调用textFieldDidEndEditing委托方法。我在viewWillDisappear方法中执行此操作。


64
投票

已经有很多答案,但仍然没有上述解决方案具有“完美”无错误,向后兼容和无闪烁动画所需的所有花哨的定位功能。 (动画帧/边界和contentOffset在一起时的错误,不同的界面方向,iPad分离键盘......) 让我分享我的解决方案: (假设你已经设置了self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)

UIKeyboardWill(Show|Hide)Notification
© www.soinside.com 2019 - 2024. All rights reserved.