当键盘存在时,如何使 UITextField 向上移动 - 开始编辑?

How can I make a UITextField move up when the keyboard is present - on starting to edit?

提问人: 提问时间:7/15/2009 最后编辑:25 revs, 15 users 42%philfreo 更新时间:6/26/2023 访问量:651335

问:

使用 iOS SDK:

我有一个 with s 可以调出一个键盘。我需要它能够:UIViewUITextField

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

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

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

我需要 a 和 a 吗?一个进去另一个吗?UIViewUIScrollView

为了自动滚动到活动文本字段,需要实现什么?

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

注意:我正在使用的(或)是由一个标签栏()调出的,它需要正常工作。UIViewUIScrollViewUITabBar


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

我已经让它工作了,当我改变键盘上下时的框架大小。我只是使用: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 UIDackScrollView UITtextField UIConponent

评论

0赞 Pankaj Kainthla 2/2/2011
您可以在 TextField 上使用动画,以便 TextField 在键盘出现时移动
10赞 Mongi Zaidi 3/26/2014
到目前为止,我发现的最好的方法是这个开源的 TPKeyboardAvoid
24赞 7/31/2013
它被苹果记录下来,我认为这是最好的方式:developer.apple.com/library/ios/#documentation/StringsTextFonts/......
62赞 Pradeep Mittal 2/10/2014
使用此代码。您只需要 appdelegate.m 文件中的 1 行即可。github.com/hackiftekhar/IQKeyboardManager
7赞 Aruna 8/20/2011
看看这个。对你来说没有麻烦。TPKeyboard避免

答:

1074赞 8 revs, 8 users 66%RPDP #1
  1. 仅当您现在拥有的内容不适合 iPhone 屏幕时,您才需要一个。(如果您添加 作为组件的超级视图只是为了在键盘出现时向上滚动,则不需要它。ScrollViewScrollViewTextField

  2. 防止 s 被键盘覆盖的标准方法是在显示键盘时向上/向下移动视图。TextField

下面是一些示例代码:

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

评论

3赞 Cocoa Dev 12/15/2010
_textField什么?我把它复制到我的代码中,它说_textField未声明。
0赞 patrick 1/28/2011
这是你用来说“当用户在这里编辑时,视图应该向上滑动”的字段,或者其他什么......但是,如果有更多字段,则可以将其删除。
0赞 Abduliam Rehmanius 6/26/2011
在 keyBoardWillSHow 和 KeyBoardWillHide 事件中调用 -(void)setViewMovedUp:(BOOL)movedUp 不是很糟糕吗!!
4赞 FractalDoctor 12/3/2012
如果支持主视图的旋转,则不是特别有用。
2赞 avance 9/22/2016
为了完成这项工作,我不得不注释掉该部分。textFieldDidBeginEditing
49赞 2 revs, 2 users 67%Jonathan Sterling #2

要考虑的一件事是您是否想单独使用 a。我还没有遇到任何设计精良的 iPhone 应用程序,这些应用程序实际上在 .UITextFieldUITextFieldsUITableViewCells

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

评论

1赞 Peter Johnson 5/21/2013
我的一个应用程序需要允许用户添加自由格式的注释 - 所以是的,有时使用 UITextField 很有用。
1赞 RJH 10/29/2015
我同意这种方法。以这种方式零工作或编码。即使您需要自由格式的注释,您仍然可以使用表格单元格
0赞 SwiftArchitect 2/10/2016
UITableView可悲的是,这是唯一的出路。键盘通知很脆弱,并且会随着时间的推移而变化。Stack Overflow 示例代码:stackoverflow.com/a/32390936/218152
0赞 Fattie 12/1/2017
这个答案已经过时了大约五年。唯一的现代解决方案是这样的......stackoverflow.com/a/41808338/294884
23赞 3 revs, 3 users 50%Peter Mortensen #3

我不确定向上移动视图是否是正确的方法。我以不同的方式完成了它,调整了 UIScrollView 的大小。我在一篇小文章中详细解释了它。

评论

3赞 Luke 7/20/2019
文章的链接已失效。
31赞 Steve #4

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

难道他的以下代码不应该使视图恢复正常吗?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
24赞 2 revs, 2 users 80%Nicolas Marchand #5

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

-(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];
    }
}
32赞 2 revs, 2 users 73%tt.Kilew #6

适用于许多 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];
}

评论

0赞 SpaceDust__ 6/17/2012
rect.origin.y=+currTextField.frame.origin.y工作正常谢谢
12赞 3 revsSteve McFarlin #7

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

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

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

UIView -> UIScrollView -> [UI Components]

黑客来了

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

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

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

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

举个例子。

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

此方法不会像其他方法那样“向后滚动”。这不是必需的。同样,这是针对一个相当“高大”的 UIView,我没有几天时间学习内部布局引擎。

450赞 13 revs, 10 users 60%Shiun #8

我在创作多个作品时也遇到了很多问题,其中一个或多个在编辑时会被键盘遮挡。UIScrollViewUITextFields

如果您没有正确滚动,需要考虑以下几点。UIScrollView

1) 确保您的 contentSize 大于帧大小。理解的方法是,它就像 contentSize 中定义的内容的查看窗口。因此,当 为了在任意位置滚动时,contentSize 必须大于 .否则,不需要滚动,因为 contentSize 中定义的所有内容都已经可见。顺便说一句,默认 contentSize = .UIScrollViewUIScrollViewsUIScrollViewUIScrollviewUIScrollViewCGSizeZero

2)既然您了解了“内容”实际上是窗口,那么确保键盘不会遮挡您的查看“窗口”的方法是调整大小,以便当键盘存在时,窗口的大小仅为原始frame.size.height减去键盘的高度。这将确保您的窗口只有很小的可视区域。UIScrollViewUIScrollView'sUIScrollViewUIScrollViewUIScrollView

3)这里有一个问题:当我第一次实现它时,我认为我必须获取编辑的文本字段并调用scrollRecToVisible方法。我通过调用该方法实现了该方法。这实际上有一个奇怪的副作用,即滚动会捕捉到位。在很长一段时间里,我都无法弄清楚那是什么。然后我注释掉了 Delegate 方法,一切都有效了!(???).事实证明,我相信实际上隐式地将当前编辑的内容隐式地带入了可视窗口。我对该方法的实现和随后对 的调用是多余的,并且是奇怪的副作用的原因。CGRectUIScrollView'sUITextFieldDelegatetextFieldDidBeginEditingscrollRecToVisibleUITextFieldtextFieldDidBeginEditingUIScrollViewUITextFieldUITextFieldDelegatescrollRecToVisible

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

// 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
  3. 确保 已设置且大于 your atcontentSizeUIScrollViewviewDidLoad
  4. 当键盘存在时缩小UIScrollView
  5. 当键盘消失时恢复UIScrollView
  6. 使用 ivar 检测键盘是否已经显示在屏幕上,因为每次 a 时都会发送键盘通知,即使键盘已经存在,以避免在键盘已经缩小时缩小 UITextFieldUIScrollView

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

希望这段代码能为你们中的一些人省去很多麻烦。

评论

3赞 Martin Wickman 4/21/2011
很好,但有两个问题:1.已弃用。2. keyboardSize 位于“屏幕坐标”中,因此如果旋转或缩放帧,则 viewFrame 计算将失败。UIKeyboardBoundsUserInfoKey
22赞 sottenad 7/31/2011
@Martin Wickman - 使用代替已弃用的CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;UIKeyboardBoundsUserInfoKey
1赞 4/19/2012
嗨,我做了同样的事情,但是文本视图只有在用户开始输入时才会向上移动?是预期的行为还是我遗漏了什么?
3赞 j7nn7k 5/24/2012
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size应该是 .不过很棒的解决方案![[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size
1赞 AlexChaffee 2/13/2013
我喜欢你的解决方案,但我想我可以让它更简单:不要为通知观察者的东西而烦恼;而是在适当的委托方法中调用正确的动画例程 -- 对于 UITextView,它们是 textViewDidBeginEditing 和 textViewDidEndEditing。
48赞 3 revs, 2 users 88%Mihai Damian #9

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

编辑:注意到示例中有一个小故障。您可能希望收听 for 而不是 .否则,在键盘关闭动画期间,键盘后面的滚动视图将被剪裁。UIKeyboardWillHideNotificationUIKeyboardDidHideNotification

18赞 2 revs, 2 users 84%user436179 #10

@user271753

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

-(BOOL)textFieldShouldReturn:(UITextField *)textField{
   [textField resignFirstResponder];
   [self setViewMovedUp:NO];
   return YES;
}
64赞 2 revs, 2 users 86%cbranch #11

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

评论

0赞 Leo 12/30/2019
不是 UIScrollView 将编辑后的 UITextField 隐式滚动到视图中,而是 UITextField,它调用私有方法,而私有方法又在调用时调用。请参见 github.com/leopatras/ios_textfields_on_scrollview。如果约束和视图控制器设置正确,实际上不需要显式调用(至少从 IOS 11 开始)。[UITextField scrollTextFieldToVisibleIfNecessary][UIScrollView scrollRectToVisible][UITextField becomeFirstResponder]scrollRectToVisible
0赞 Zaraki 9/28/2021
UITextView是否可能实现这样的事情,或者我们应该手动处理吗?
275赞 4 revs, 4 users 71%DK_ #12

实际上,最好只使用 Apple 的实现,如文档中提供的那样。但是,他们提供的代码是错误的。将注释下方的部分替换为以下内容: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];
}

苹果代码的问题如下: (1) 他们总是计算该点是否在视图的框架内,但它是一个 ,所以它可能已经滚动了,你需要考虑该偏移量:ScrollView

origin.y -= scrollView.contentOffset.y

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

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

评论

3赞 mblackwell8 5/9/2013
在滚动视图未填满屏幕的情况下,应将 aRect 设置为滚动视图的框架
2赞 htafoya 4/16/2014
您不应该想要 CGPoint origin = activeField.frame.origin + activeField.frame.size.height 吗?,因为您希望显示整个文本字段,如果它恰好只有一些可见像素,那么代码将不会进入条件。
1赞 Andrew 4/27/2014
此解决方案在横向下不起作用 - 文本字段从视口顶部飞出。装有 iOS 7.1 的 iPad。
4赞 Endareth 11/13/2014
为了更好地支持 iOS 8,我建议在获取键盘尺寸时使用而不是使用,因为这将获取诸如自定义键盘更改和打开/关闭预测文本之类的内容。UIKeyboardFrameEndUserInfoKeyUIKeyboardFrameBeginUserInfoKey
1赞 Morten Holmgaard 1/23/2015
@Egor:您的修复使它更好地工作 - 但最后一行必须是相反的:self.scrollView.contentOffset = self.currentSVoffset;
10赞 4 revs, 4 users 75%lakersgonna3peat #13

何时在滚动中应自动设置。UITextFieldUITableViewCell

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

例如,当我重新加载我的长表时,底部有一个,如下所示,UITextField

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

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

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

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

评论

0赞 Adam Johns 8/9/2014
我很困惑这个代码是做什么用的?显示键盘时,不被调用。并且不会使被遮挡的行变得可见。viewWillAppearreloadData
102赞 5 revs, 4 users 60%Michael Tyson #14

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

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

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

这里是:将文本字段移出键盘的解决方案

评论

0赞 Almo 11/23/2014
像我的滚动视图这样奇怪的东西是否都适合屏幕,所以它无法滚动。打开和关闭键盘后,内容现在更大了(看起来像是添加了一些不可见的东西,而不是在页面底部删除),并且可以滚动。
253赞 8 revs, 8 users 26%Volodymyr Kulyk #15

In 和 in 调用函数,如下所示: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];
}

我希望这段代码能对你有所帮助。

斯威夫特 5

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.animate(withDuration: movementDuration, delay: 0, options: [.beginFromCurrentState]) {
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
    }
}

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

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

评论

1赞 Andre Cytryn 8/9/2012
为什么不使用?[UIView animateWithDuration: animations:^{ }];
2赞 Hammer 10/10/2014
这很好用,尽管 const int movementDistance = -130;根据需要进行调整,使其更加灵活
7赞 James Perih 5/9/2015
在小型实现中非常简单。没有 ScrollViews 和模棱两可的自动布局问题。
6赞 stk 10/23/2016
呃......您根本不使用 textField 参数。那么为什么要将其作为函数参数呢?此外,您也可以在 Swift 中使用三元运算符。使代码不那么说话。
1赞 bvmobileapps 3/22/2017
如果视图的背景颜色不是黑色,请确保将窗口的颜色设置为与视图匹配,以便用户看不到视图后面。即 self.window.backgroundColor = [UIColor whiteColor];
16赞 2 revs, 2 users 86%progrmr #16

它不需要滚动视图即可移动视图框。您可以更改视图的框架,使整个视图向上移动,刚好足以将 firstresponder 文本字段放在键盘上方。当我遇到这个问题时,我创建了一个子类来做到这一点。它观察键盘将出现通知并找到第一响应者子视图,并且(如果需要)它将主视图向上动画,以便第一响应者位于键盘上方。当键盘隐藏时,它会将视图动画化回原来的位置。viewcontroller'sUIViewController

要使用此子类,请使自定义视图控制器成为 GMKeyboardVC 的子类,并且它继承了此功能(只需确保实现并且它们必须调用 super)。该类位于 github 上。viewWillAppearviewWillDisappear

评论

0赞 jaime 9/14/2013
什么许可证?你的一些文件有开源许可证,有些没有。
0赞 Almo 11/23/2014
警告:此代码对 ARC 项目不友好。
0赞 progrmr 11/24/2014
您只需添加构建选项以指定这些是非 ARC 文件,或者欢迎将其转换为 ARC 并提交拉取请求。
10赞 3 revs, 2 users 95%savagenoob #17

一直在为初学者寻找有关该主题的好教程,在这里找到了最好的教程。

在本教程底部的示例中,请务必在MIScrollView.h

@property (nonatomic, retain) id backgroundTapDelegate;

如你所见。

评论

0赞 Maarten Bodewes 1/2/2012
嗨,savagenoob,感谢您提供的链接,欢迎来到stackoverflow。在回答(未来的)问题时,请尝试提供尽可能多的信息 - 简单的链接有点脆弱。也就是说,如果答案是一个可能被忽略的好教程的链接。
7赞 2 revs, 2 users 89%deepti #18

您需要以编程方式添加具有特定帧大小的滚动视图。您必须添加 .h 文件。您必须启用 scrollview,您需要在 viewDidLoad() 中编写以下内容。UIScrollViewDelegate

scrollview.scrollEnabled=YES;
scrollview.delegate=self;

scrollview.frame = CGRectMake(x,y,width,height);
//---set the content size of the scroll view--- 

[scrollview setContentSize:CGSizeMake(height,width)];

这样,您可以添加 x、y、宽度和高度值。

5赞 Matt Weaver #19

一个更优雅的解决方案是使用子类(尽管这并不总是合适的)并在父帧更改时重新计算所有子视图(并且要聪明:仅在新帧大小更改时重新计算它们,即用于在覆盖时和调用之前比较新帧)。唯一的问题是,你打算使用的应该监听键盘事件(或者,你可以在它本身中这样做,以便于封装)。但只有 和 .这只是为了让它看起来很流畅(如果你等待 CG 调用它,你会得到片刻的断断续续)。UIViewCGRectEqualToRectsetFrame[super setFrame:frame_]UIViewControllerUIViewUIKeyboardWillShowNotificationUIKeyboardWillHideNotification

这样做的好处是,无论如何,它都可以构建一个做正确事情的子类。UIView

幼稚的实现是覆盖(不),更好的实现是只使用(然后在 中,或者你可以在 SINGLE 方法中调用 [view ],该方法被调用用于 show 或 hide)。drawRect:layoutSubviewsUIViewControllersetNeedsLayout

此解决方案摆脱了对键盘偏移量进行硬编码(如果它们不在拆分等中,则会更改),并且还意味着您的视图可以是许多其他视图的子视图,并且仍然可以正确响应。

除非没有其他解决方案,否则不要对类似的东西进行硬编码。如果你做对了,操作系统会给你足够的信息,你只需要智能地重新绘制(基于你的新大小)。这更干净,也是你应该做事的方式。(不过,可能还有更好的方法。frame

干杯。

7赞 2 revs, 2 users 74%user1105624 #20

试试这个:

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:self.m_Sp_Contact])
    {
        [self.m_Scroller setContentOffset:CGPointMake(0, 105)animated:YES];          
    }
}
1赞 user1240409 #21

请按照以下步骤操作,这可能会有所帮助。 放置一个视图,然后将文本字段放在该视图上,并在键盘启动时通过委托检测事件,此时立即为视图添加动画(您也可以为该视图分配一些位置),然后您的视图将上升到该位置。对视图进行动画处理时,执行相同的操作。

谢谢

12赞 2 revsDheeraj V.S. #22

根据文档,从 iOS 3.0 开始,当对文本字段进行内联编辑时,该类会自动调整其表格视图的大小并重新定位。我认为像一些人指出的那样,将文本字段放在 a 中是不够的。UITableViewControllerUITableViewCell

文档中

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

评论

0赞 Morpheus78 12/13/2016
我找到了同样的评论。是的,这是真的。奇怪的是,它在一个 UITabelViewController 中工作,而在第二个 UITabelViewController 中不起作用。但我在我的实现中找不到任何差异。
19赞 HotJard #23

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

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

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

评论

0赞 Bala 10/8/2014
嗨,Hotjard,我在 [self.view addSubview:natView] 中得到了EXE_BAD_ACCESS;
2赞 Nestor #24

这里有很多答案,但这有效并且比大多数答案短得多:

- (void)textFieldDidBeginEditing:(UITextField *)sender
{
    UIScrollView *scrollView = (UIScrollView *)self.view; // assuming this method is pasted into the UIScrollView's controller
    const double dontHardcodeTheKeyboardHeight = 162;
    double textY = [sender convertPoint:CGPointMake(0, 0) toView:scrollView].y;
    if (textY - scrollView.contentOffset.y + sender.frame.size.height > self.view.frame.size.height - dontHardcodeTheKeyboardHeight)
        [scrollView setContentOffset:CGPointMake(0.0, textY - 10) animated:YES];
}
136赞 DAS #25

仅使用 TextFields:

1a) 使用:选择所有文本字段 => 编辑 =>嵌入 => ScrollViewInterface Builder

1b) 在 UIScrollView 中手动嵌入 TextFields,称为 scrollView

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

评论

8赞 TheTiger 5/5/2014
但是,当已经可见时,它也会向上移动视图。textField
1赞 Jun 1/11/2015
需要更改为CGPointMake(0, textField.frame.origin.y);CGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
0赞 rak appdev 4/4/2017
@Egor 即使在您发表评论之后,它也不起作用。就像提到的“TheTiger”一样,即使在文本字段可见之后,它也会向上移动视图。
0赞 tibalt 5/8/2019
XCode 10 的更改:“选择所有文本字段 => 编辑器 => 嵌入 => 滚动视图”
4赞 2 revs, 2 users 74%Ganesh Guturi #26

您也可以使用 textfield 委托方法执行此操作。检查下面的代码。将文本字段放置在滚动视图上时,它对我有用。

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

注意:您必须根据您的视图更改 cPoint.y - 100 值。

6赞 3 revs, 3 users 62%user966931 #27

这是一个用于键盘处理的免费库 iPhone-Applications 中的键盘处理。你只需要写一行代码:

[AutoScroller addAutoScrollTo:scrollView];

这在表单中处理键盘真是太棒了

评论

0赞 quantumpotato 1/14/2013
注册下载? ..不,谢谢。请托管在 github 或类似网站上。
0赞 George 5/23/2013
不错,但我对这个库有问题。代码不在 github 上,这真是太可惜了。但无论如何,谢谢。
67赞 3 revsMartin Ullrich #28

已经有很多答案,但上面的解决方案仍然没有一个具有“完美”无错误、向后兼容和无闪烁动画所需的所有花哨的定位内容。(将帧/边界和 contentOffset 动画在一起时的错误,不同的界面方向,iPad 拆分键盘,...
让我分享我的解决方案:
(假设您已经设置
UIKeyboardWill(Show|Hide)Notification)

// 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];
}

评论

0赞 Lucien 6/6/2013
@Shiun答案的巨大改进。但是键盘消失后,视图不会回到第一个位置。这仍然是一部伟大的作品:)
2赞 xaphod 6/3/2017
谢谢,这是我2017年最好的解决方案。请注意,您不需要自己跟踪 focusedControl,您可以使用 来确定。这是 Swift 3 版本的答案(减去 willHide 部分),并实现了: gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72dUIApplication.shared.sendAction(...)sendAction
0赞 Martin Ullrich 6/26/2017
@xaphod就我而言,我需要关注更多的控件 - 例如,输入字段下方的按钮。但是,是的,该代码现在已经有 4 年的历史了,可能会从改进中受益。
0赞 X.Y. 1/27/2019
这可能是正确的解决方案。键盘通知携带动画数据,文本字段委托不知道动画持续时间,这只是猜测工作。
3赞 Himanshu Mahajan #29

请按照下列步骤操作。

1) 在 .h 文件中声明以下变量。

  {      
         CGFloat animatedDistance;
  }

2) 在 .m 文件中声明以下常量。

  static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
  static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
  static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
  static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
  static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 162;

3) 使用 UITextField 委托向上/向下移动键盘。

  -(void) textFieldDidBeginEditing:(UITextField *)textField
  { 
         if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
         {
               CGRect textFieldRect = [self.view.window convertRect:textField.bounds fromView:textField];
               CGRect viewRect = [self.view.window convertRect:self.view.bounds fromView:self.view];

               CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
               CGFloat numerator =
    midline - viewRect.origin.y
    - MINIMUM_SCROLL_FRACTION * viewRect.size.height;
               CGFloat denominator =
    (MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION)
    * viewRect.size.height;
               CGFloat heightFraction = numerator / denominator;

               if (heightFraction < 0.0)
               {
                     heightFraction = 0.0;
               }
               else if (heightFraction > 1.0)
               {
                     heightFraction = 1.0;
               }

               UIInterfaceOrientation orientation =
    [[UIApplication sharedApplication] statusBarOrientation];
               if (orientation == UIInterfaceOrientationPortrait)
               {
                     animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
               }
               else
               {
                     animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
               }

               CGRect viewFrame = self.view.frame;
               viewFrame.origin.y -= animatedDistance;

               [UIView beginAnimations:nil context:NULL];
               [UIView setAnimationBeginsFromCurrentState:YES];
               [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];

               [self.view setFrame:viewFrame];

               [UIView commitAnimations];
       }
  }

  -(void) textFieldDidEndEditing:(UITextField *)textField
  {
       if(UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone)
       {
             CGRect viewFrame = self.view.frame;
             viewFrame.origin.y += animatedDistance;

             [UIView beginAnimations:nil context:NULL];
             [UIView setAnimationBeginsFromCurrentState:YES];
             [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];

             [self.view setFrame:viewFrame];

             [UIView commitAnimations];
       }
 }

评论

0赞 koen 10/10/2013
这个答案是直接从 cocoawithlove.com/2008/10/ 复制而来的......
121赞 6 revs, 3 users 91%Mohd Iftekhar Qurashi #30

对于通用解决方案,这是我实现 IQKeyboardManager 的方法。

enter image description here

步骤1:-我在单例类中添加了 、 和 的全局通知。我称之为 IQKeyboardManagerUITextFieldUITextViewUIKeyboard

步骤2:-如果找到或通知,我会尝试从层次结构中获取实例。为了正确揭开/在其上,需要调整框架。UIKeyboardWillShowNotificationUITextFieldTextDidBeginEditingNotificationUITextViewTextDidBeginEditingNotificationtopMostViewControllerUIWindow.rootViewControllerUITextFieldUITextViewtopMostViewController.view

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

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

步骤5:-如果找到或通知,我再次尝试从层次结构中获取实例。UIKeyboardWillHideNotificationUITextFieldTextDidEndEditingNotificationUITextViewTextDidEndEditingNotificationtopMostViewControllerUIWindow.rootViewController

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

步骤7:-我根据被打扰的距离恢复了下来。topMostViewController.view.frame

步骤8:-我在应用程序加载时实例化了单例 IQKeyboardManager 类实例,因此应用程序中的每个 / 都会根据预期的移动距离自动调整。UITextFieldUITextView

这就是IQKeyboardManager为您所做的一切,真的没有代码行!只需将相关的源文件拖放到项目中即可。IQKeyboardManager 还支持设备方向自动 UIToolbar 管理KeybkeyboardDistanceFromTextField 以及比您想象的要多得多的功能。

评论

0赞 user3722523 11/6/2015
将 IQKeyBoardManagerSwift 目录添加到我的项目中,但不起作用。无法启用,因为它在 AppDelegate 中无法识别...
2赞 Brian Bird 1/6/2018
这感觉就像是网络钓鱼,没有显示实际的解决方案,而是我们看到这个家伙的 GitHub 帐户的商业广告。
2赞 Sebastian Roth #31

刚刚找到这个类:

https://github.com/OliverLetterer/SLScrollViewKeyboardSupport

到目前为止,它在iPhone上运行良好,包括动画和正确的偏移。

要使用它,只需添加到:viewDidLoad

self.support = [[SLScrollViewKeyboardSupport alloc] initWithScrollView:self.scrollView];
2赞 2 revsPKN #32
  • 如果文本字段没有完全或部分隐藏,那么我们不应该更改任何内容。
  • 我们应该计算隐藏的确切交集区域(键盘的框架和文本字段的框架),然后我们应该更改视图的框架。

  • 在这里,我举了一个完整的例子。

    声明 3 变量

#define 填充 10

@interface PKViewController ()
      @property (nonatomic, assign) CGRect originalViewFrame; //original view's frame
      @property (nonatomic, strong) UITextField *activeTextField; // current text field
      @property (nonatomic, assign) CGRect keyBoardRect; // covered area by keaboard
     @end

存储原始框架

- (void)viewDidLoad {
    [super viewDidLoad];
    _originalViewFrame = self.view.frame;
}

将视图控制器添加为键盘通知的观察者

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}

删除观察者

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

在键盘出现时将其覆盖的区域存储起来,并在键盘消失时将其设置为 CGRectZero

- (void)keyboardWasShown:(NSNotification *)notification{
    CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    _keyBoardRect = CGRectMake(0, _originalViewFrame.size.height - keyboardSize.height, keyboardSize.width, keyboardSize.height);
    [self moveTextFieldUP];

}
- (void) keyboardWillHide:(NSNotification *)notification{
    _keyBoardRect = CGRectZero;
    [self setDefaultFrame];
}

存储活动文本字段

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    _activeTextField = textField;
//When keyboard is already present but the textfield is hidden. Case:When return key of  keyboard makes the next textfield as first responder
    if (!CGRectIsEmpty(_keyBoardRect)) { 
        [self moveTextFieldUP];
    }
    return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
    [textField resignFirstResponder];
    return YES;
}

现在我们应该改变视图的框架

- (void)moveTextFieldUP{
    CGRect virtualTextFieldRect = CGRectMake(0, self.view.frame.origin.y, _activeTextField.frame.size.width, _activeTextField.frame.origin.y+_activeTextField.frame.size.height);
    if (CGRectIntersectsRect(_keyBoardRect, virtualTextFieldRect)) {
        CGRect intersectRect = CGRectIntersection(_keyBoardRect, virtualTextFieldRect);
        CGFloat newY = _originalViewFrame.origin.y - intersectRect.size.height;
        CGFloat newHeight = _originalViewFrame.size.height + intersectRect.size.height;
        CGRect newFrame = CGRectMake(0, newY-PADDING, _originalViewFrame.size.width, newHeight+PADDING);
        [UIView animateWithDuration:0.3 animations:^{
            [self.view setFrame:newFrame];
        }];

        NSLog(@"Intersect");
    }
}
- (void)setDefaultFrame {
    [UIView animateWithDuration:0.3 animations:^{
        [self.view setFrame:_originalViewFrame];
    }];
}
3赞 2 revs, 2 users 93%alexgophermix #33

我最近发现自己在开发消息传递应用程序时遇到了类似的情况。我创建了一个自定义 UIView,它粘在键盘顶部,并自动执行您需要的大部分操作

MessageComposerView


(来源: thegameengine.org

这个项目背后的想法是创建一些功能类似于 iMessage 组合视图 AKA:

  • 粘在键盘顶部,并在键盘关闭时移动到屏幕底部
  • 处理文本中的更改
  • 手柄旋转

若要调整 UIScrollView 的大小/重新配置,需要使用以下可选委托方法:

- (void)messageComposerFrameDidChange:(CGRect)frame withAnimationDuration:(float)duration;

每当帧更改(调整大小、重新定位、旋转)时,都会调用它,并且还会提供动画持续时间。您可以使用此信息根据需要调整 UIScrollView 的框架和内容插图的大小。

-2赞 catamphetamine #34

如果您仍在为此苦苦挣扎,请阅读我的帖子

我今天想出了一个解决方案。 我阅读了许多关于这个问题的帖子和“教程”,但没有一个在每种情况下都有效(其中大多数是彼此的复制粘贴)。 即使是苹果官方提出的“解决方案”也行不通,而且,它在横向模式下完全不起作用。 苹果公司没有给开发人员提供解决这样一个常见基本问题的手段,这真是太可惜了。非常不专业。如此惊人的框架(可可)和如此令人讨厌的被低估的问题。

现在,我的解决方案是:将UIScrollView设为根视图,然后将所有内容放入其中。 然后,从这个 KeyboardAwareController 类中子类化视图控制器(可能需要重新定义 scrollView 和 keyboardPadding 方法):

// KeyboardAwareController.h 社会病 // 由管理员在 13.01.14 创建。 版权所有 (c) 2014 kuchumovn。保留所有权利。 //

#import <UIKit/UIKit.h>

@interface KeyboardAwareController : UIViewController <UITextFieldDelegate>

@end

// KeyboardAwareController.m(键盘Aware控制器.m) 社会病 // 由管理员在 13.01.14 创建。 版权所有 (c) 2014 kuchumovn。保留所有权利。 //

#import "KeyboardAwareController.h"

@interface KeyboardAwareController ()

@end

@implementation KeyboardAwareController
{
    CGPoint scrollPositionBeforeKeyboardAdjustments;

    __weak UIScrollView* scrollView;

    UITextField* activeField;
}

- (id) initWithCoder: (NSCoder*) decoder
{
    if (self = [super initWithCoder:decoder])
    {
        scrollPositionBeforeKeyboardAdjustments = CGPointZero;
    }
    return self;
}

- (void) viewDidLoad
{
    [super viewDidLoad];
}

- (UIScrollView*) scrollView
{
    return (UIScrollView*) self.view;
}

- (CGFloat) keyboardPadding
{
    return 5;
}

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

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

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

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

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardDidShowNotification
                                                  object:nil];

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

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

    [self registerForKeyboardNotifications];
}

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

    [super viewWillDisappear:animated];
}

- (void) keyboardWillShow: (NSNotification*) notification
{
    //NSLog(@"keyboardWillShow");

    // force the animation from keyboardWillBeHidden: to end
    scrollView.contentOffset = scrollPositionBeforeKeyboardAdjustments;

    scrollPositionBeforeKeyboardAdjustments = CGPointZero;
}

// warning: i have no idea why this thing works and what does every line of this code mean
// (but it works and there is no other solution on the internets whatsoever)
// P.S. Shame on Apple for missing such a basic functionality from SDK (and many other basic features we have to hack and mess around with for days and nights)

- (void) keyboardDidShow: (NSNotification*) notification
{
    //NSLog(@"keyboardDidShow");

    UIWindow* window = [[[UIApplication sharedApplication] windows]objectAtIndex:0];
    UIView* mainSubviewOfWindow = window.rootViewController.view;

    CGRect keyboardFrameIncorrect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect keyboardFrame = [mainSubviewOfWindow convertRect:keyboardFrameIncorrect fromView:window];
    CGSize keyboardSize = keyboardFrame.size;

    CGRect visibleFrame = CGRectMake(0, 0, 0, 0);
    visibleFrame.origin = self.scrollView.contentOffset;
    visibleFrame.size = self.scrollView.bounds.size;

    CGFloat paddedKeyboardHeight = keyboardSize.height + self.keyboardPadding;

    //NSLog(@"visibleFrame %@", NSStringFromCGRect(visibleFrame));

    visibleFrame.size.height -= paddedKeyboardHeight;

    //NSLog(@"visibleFrame after keyboard height %@", NSStringFromCGRect(visibleFrame));

    if (CGRectContainsPoint(visibleFrame, activeField.frame.origin))
        return;

    scrollPositionBeforeKeyboardAdjustments = scrollView.contentOffset;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, activeField.frame.origin.y - visibleFrame.size.height + activeField.frame.size.height, 0);

    contentInsets = UIEdgeInsetsMake(0.0, 0.0, paddedKeyboardHeight, 0);

    self.scrollView.contentInset = contentInsets;
    self.scrollView.scrollIndicatorInsets = contentInsets;

    CGSize scrollContentSize = self.scrollView.bounds.size;
    scrollContentSize.height += paddedKeyboardHeight;
    self.scrollView.contentSize = scrollContentSize;

    //NSLog(@"scrollView %@", NSStringFromCGRect(scrollView.frame));
    //NSLog(@"activeField %@", NSStringFromCGRect(activeField.frame));

    //[scrollView scrollRectToVisible:activeField.frame animated:YES];

    CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y - visibleFrame.size.height + activeField.frame.size.height);

    //NSLog(@"scrollPoint %@", NSStringFromCGPoint(scrollPoint));

    [self.scrollView setContentOffset:scrollPoint animated:YES];
}

- (void) keyboardWillBeHidden: (NSNotification*) notification
{
    //NSLog(@"keyboardWillBeHidden");

    UIEdgeInsets contentInsets = UIEdgeInsetsZero;

    // this doesn't work when changing orientation while the keyboard is visible
    // because when keyboardDidShow: will be called right after this method the contentOffset will still be equal to the old value
    //[scrollView setContentOffset:scrollPositionBeforeKeyboardAdjustments animated:YES];

    [UIView animateWithDuration:.25 animations:^
    {
        self.scrollView.contentInset = contentInsets;
        self.scrollView.scrollIndicatorInsets = contentInsets;

        // replacement for setContentOffset:animated:
        self.scrollView.contentOffset = scrollPositionBeforeKeyboardAdjustments;
    }];
}

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

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

如果你有任何问题,我的项目托管在github上:https://github.com/kuchumovn/sociopathy.ios

为了更好地解释,我还截取了一张截图:see the storyboard layout screenshot

评论

2赞 Zoltán 7/16/2014
如果能从对这个答案投反对票的人那里得到一些反馈,那将是有用的。
0赞 2 revsxaphod #35

我在这里没有看到这种可能性,所以我添加了它,因为我尝试了答案中的方法,但几个小时后发现 iOS5/6 中的 XCode 7 有一种更简单的方法: 使用 NSLayoutConstraints。

请参见: 自动布局约束 - 键盘

这是我的代码:

.m 文件:

// Called when the UIKeyboardWillShowNotification is sent.
- (void)keyboardWillBeShown:(NSNotification*)aNotification
{
    NSLog(@"keyboardWillBeShown:");
    [self.PhoneNumberLabelOutlet setHidden:TRUE];
    CGFloat heightOfLabel = self.PhoneNumberLabelOutlet.frame.size.height;
    for( NSLayoutConstraint* thisConstraint in self.topElementsVerticalDistanceFromTopLayoutConstraint ) {
        thisConstraint.constant -= heightOfLabel;
    }

    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    CGFloat oldConstant = [self.SignInYConstraint constant];
    self.SignInYConstraint.constant = oldConstant + kbSize.height;
    [self.view setNeedsUpdateConstraints];

    NSTimeInterval duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    [UIView animateWithDuration:duration animations:^{
        [self.view layoutIfNeeded];
    }];

}

.h 文件:

#import <UIKit/UIKit.h>

@interface SignInViewController : UIViewController {

    UITextField* _activeField;
}




- (void)signInCallback:(NSObject*)object;


@property (weak, nonatomic) IBOutlet UILabel *PhoneNumberLabelOutlet;

@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *ActivityIndicatorOutlet;

@property (weak, nonatomic) IBOutlet UITextField *UserIDTextfieldOutlet;

@property (weak, nonatomic) IBOutlet UITextField *PasswordTextfieldOutlet;

@property (weak, nonatomic) IBOutlet UIButton *SignInButton;

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

@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *topElementsVerticalDistanceFromTopLayoutConstraint;

@end
0赞 2 revsErik Allen #36

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

它也没有考虑到风景,这就是我一开始遇到麻烦的地方。玩了几天后,这就是我的方法。keyboardWasShown:

- (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 作为我的宽度。

最终对我有用的布局是这些:

  • 将视图滚动到超级视图:基本上,我给顶部、左侧和右侧提供了约束。
    • Horizontal Space - View - Scroll View(0)
    • Vertical Space - View - Scroll View(0)
    • Horizontal Space - Scroll View - View(0)
  • 滚动视图高度:我将滚动视图设置为恒定高度。我不知道这是否真的有必要,但它得到了滚动视图本身的界限。
    • Height - (248) - Scroll View
  • 滚动视图的内容视图:我为所有侧面提供了常量,顶部、左侧、底部和右侧。
    • Vertical Space - View - Scroll View(0)
    • Vertical Space - Scroll View - View(0)
    • Horizontal Space - View - Scroll View(0)
    • Horizontal Space - Scroll View - View(0)
  • 内容视图的维度。
    • Height - (248) - View
    • Width - (320) - View
0赞 PVCS #37

在 (BOOL)textFieldShouldBeginEditing:(UITextField *)textField 中

if (textField.frame.origin.y > self.view.frame.size.height - 216)
    {
        if (screenHeight>500)
            scrollView.contentSize = CGSizeMake(0.0, scrollView.contentSize.height + 100);
        else
            scrollView.contentSize = CGSizeMake(0.0, scrollView.contentSize.height + 216);
        CGPoint scrollPoint = CGPointMake(0.0,(textField.frame.origin.y - (self.view.frame.size.height - 216 - textField.frame.size.height - 20)));
        [scrollView setContentOffset:scrollPoint animated:YES];
    }
    [scrollView setScrollEnabled:YES];

在重新登录keyBoard时,需要编写以下代码

scrollView.contentSize = CGSizeMake(0.0, 640);
CGPoint scrollPoint = CGPointMake(0.0,0.0);
[scrollView setContentOffset:scrollPoint animated:YES]; 
4赞 4 revsFonix #38

这是我制作的 UITextfield(和其他类似字段)类别,它将使 TextField 避开键盘,您应该能够将其按原样拖放到视图控制器中,它应该可以工作。它将整个屏幕向上移动,使当前文本字段位于带有动画的键盘上方

#import "UIView+avoidKeyboard.h"
#import "AppDelegate.h"

@implementation UIView (avoidKeyboard)

- (void) becomeFirstResponder {

if(self.isFirstResponder)
    return;

[super becomeFirstResponder];

if ([self isKindOfClass:[UISearchBar class]] ||
    [self isKindOfClass:[UITextField class]] ||
    [self isKindOfClass:[UITextView class]])
{
    AppDelegate *appDelegate    = [UIApplication sharedApplication].delegate;

    CGRect screenBounds         = appDelegate.window.frame;

    CGFloat keyboardHeight;
    CGFloat keyboardY;
    CGFloat viewsLowestY;
    CGPoint origin              = [self.superview convertPoint:self.frame.origin toView:appDelegate.window]; //get this views origin in terms of the main screens bounds

    if(UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])){ //the window.frame doesnt take its orientation into account so if its sideways we must use the x value of the origin instead of the y
        keyboardHeight          = 216;
        keyboardY               = screenBounds.size.height  - keyboardHeight; //find the keyboards y coord relative to how much the main window has moved up
        viewsLowestY            = origin.y + self.frame.size.height; //find the lowest point of this view
    }
    else {
        keyboardHeight          = 162;
        keyboardY               = screenBounds.size.width  - keyboardHeight;
        viewsLowestY            = origin.x + self.frame.size.height;
    }

    CGFloat difference          = viewsLowestY - keyboardY + 20; //find if this view overlaps with the keyboard with some padding

    if (difference > 0){ //move screen up if there is an overlap

        [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{

            CGRect frame = appDelegate.window.frame;

            if(UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])){
                frame.origin.y -= difference;
            }
            else {
                frame.origin.x -= difference;
            }
            appDelegate.window.frame = frame;
        }
        completion:nil];
    }
}
}

//look at appDelegate to see when the keyboard is hidden

@end

在 appDelegate 中添加此函数

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardHides:) name:UIKeyboardWillHideNotification object:nil]; //add in didFinishLaunchingWithOptions

...

- (void)keyboardHides:(NSNotification *)notification
{
    [UIView animateWithDuration:0.3 animations:^{
        [window setFrame: CGRectMake(0, 0, window.frame.size.width, window.frame.size.height)];
    } completion:nil];
}
2赞 2 revs, 2 users 67%Muhammad Mohsin Najmuddin #39
-(BOOL) textFieldShouldBeginEditing:(UITextField *)textField {

  [self slideUp];
   return YES;
}

-(BOOL) textFieldShouldEndEditing:(UITextField *)textField {

    [self slideDown];
   return YES;
}

#pragma mark - Slide Up and Down animation

- (void) slideUp {
    [UIView beginAnimations:nil context:nil];
    layoutView.frame = CGRectMake(0.0, -70.0, layoutView.frame.size.width, layoutView.frame.size.height);

    [UIView commitAnimations];
}


- (void) slideDown {
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDelay: 0.01]; 
    layoutView.frame = CGRectMake(0.0, 0.0, layoutView.frame.size.width, layoutView.frame.size.height);
    [UIView commitAnimations];
}

评论

0赞 Ravi Ojha 3/17/2015
这里什么是layoutView??
8赞 Juraj Petrik #40

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

我更喜欢使用 scrollContentInset 和 scrollContentOffset 来处理这个问题,而不是弄乱我的视图框架。

首先,让我们听听键盘通知

//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];
}

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

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

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

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

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

最后,这就是魔力

- (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;
}
4赞 vinksharma #41

有很多答案可以告诉这种方法。我采取了同样的方法,但实施效果不佳。

这是基本的想法。我对keyboardWasShows方法进行了修改。

{
// Obtain keyboard Info
NSDictionary* info = [notification userInfo];
CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];

// Obtain ScrollView Info w.r.t. top View
CGRect scrollViewRect = [self.view convertRect:self.scrollView.frame fromView:nil];

// Depending upon your screen Ui, Scroll View's bottom edge might be at some offset from screen's bottom
// Calculate the exact offset
int scrollViewBottomOffset = self.view.frame.size.height - (scrollViewRect.origin.y + scrollViewRect.size.height);
int heightToBeAdjusted = keyboardRect.size.height - scrollViewBottomOffset;


// We may also need to consider the Insets if already present with ScrollView. Let's keep it simple for now
// But we should store these, so that we can restore the Insets when Keyboard is gone
// origInsets = self.scrollView.contentInset;

// Set the new Insets for ScrollView
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, heightToBeAdjusted, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;

// Visible frame (not overlapped by Keyboard)
CGRect visibleFrame = self.view.frame;
visibleFrame.size.height -= keyboardRect.size.height;

// Get the Rect for Textfield w.r.t self.view
CGRect activeFieldFrame = self.activeField.frame;
activeFieldFrame = [self.view convertRect:activeFieldFrame fromView:self.scrollView];

// Check if the TextField is Visible or not
if (!CGRectContainsRect(visibleFrame, activeFieldFrame) ) {
    // Scroll to make it visible but for scrolling use the activeField frame w.r.t. to scroll View
    [self.scrollView scrollRectToVisible:self.activeField.frame animated:YES];
}

}

并添加此方法初始化 activeField

- (IBAction)textFieldDidBeginEditing:(UITextField *)sender
{
self.activeField = sender;
}

评论

0赞 Axort 1/23/2015
如何恢复到原来的状态?我试过 UIEdgeInsets contentInsets = UIEdgeInsetsZero;self.scrollView.contentInset = 内容插图;self.scrollView.scrollIndicatorInsets = 内容插图;就像在示例中一样,但没有成功。编辑:我用这个解决了它:[self.scrollView setContentOffset:CGPointMake(0, 0)];可以吗?
4赞 anand madhav #42

https://github.com/michaeltyson/TPKeyboardAvoiding 下载此文件并在表视图中添加自定义类,它将管理所有事情,因为您不需要做任何事情。 它有很多选项,你可以检查一下其他的,这就是你避免键盘所需要的一切

5赞 2 revsdulgan #43

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

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

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

2) 设置以下约束:

  • 从顶部到顶部布局指南 : 0
  • S 到底部布局指南 : 0
  • S 导致超视图 : 0
  • S 尾随到上视图 : 0

  • V 顶部空间 : 0

  • 五、底下空间:0
  • V 尾随空间到超视图 : 0
  • 五、超前空间:0

  • V 与 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];
}

还有tadaaaaa,它有效!

33赞 Zar E Ahmer #44

找到最简单的解决方案

- (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];
}

评论

0赞 MELWIN 11/13/2014
屏幕向上移动,即使它不在底部。也就是说,如果文本字段在顶部,它就会移出屏幕。如何控制这种情况?
0赞 8/3/2016
@MELWIN 只需在以下行后添加: 请不要说该变量是弹出的键盘的 CGRect,您可以通过执行以下操作获得:int movement = (up ? -movementDistance : movementDistance);if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 }keyboardlet keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
8赞 2 revsHomam #45

这是使用 Swift 的解决方案。

import UIKit

class ExampleViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var scrollView: UIScrollView!

    @IBOutlet var textField1: UITextField!
    @IBOutlet var textField2: UITextField!
    @IBOutlet var textField3: UITextField!
    @IBOutlet var textField4: UITextField!
    @IBOutlet var textField5: UITextField!

    var activeTextField: UITextField!

    // MARK: - View
    override func viewDidLoad() {
        super.viewDidLoad()
        self.textField1.delegate = self
        self.textField2.delegate = self
        self.textField3.delegate = self
        self.textField4.delegate = self
        self.textField5.delegate = self
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.registerForKeyboardNotifications()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        self.unregisterFromKeyboardNotifications()
    }

    // MARK: - Keyboard

    // Call this method somewhere in your view controller setup code.
    func registerForKeyboardNotifications() {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
        center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterFromKeyboardNotifications () {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil)
        center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }

    // Called when the UIKeyboardDidShowNotification is sent.
    func keyboardWasShown (notification: NSNotification) {
        let info : NSDictionary = notification.userInfo!
        let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size

        let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;

        // If active text field is hidden by keyboard, scroll it so it's visible
        // Your app might not need or want this behavior.
        var aRect = self.view.frame
        aRect.size.height -= kbSize.height;
        if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
            self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true)
        }
    }

    // Called when the UIKeyboardWillHideNotification is sent
    func keyboardWillBeHidden (notification: NSNotification) {
        let contentInsets = UIEdgeInsetsZero;
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    // MARK: -  Text Field

    func textFieldDidBeginEditing(textField: UITextField) {
        self.activeTextField = textField
    }

    func textFieldDidEndEditing(textField: UITextField) {
        self.activeTextField = nil
    }

}

评论

0赞 Thiha Aung 1/4/2017
正确答案,但是我在使用TextField和TextView时都没有问题。有什么帮助吗?
0赞 Homam 1/4/2017
@Thiha Aung,源代码中的IBOutlet变量是否连接到IB?
0赞 Thiha Aung 1/4/2017
是的,它们也是连接的。在该行使用 UITextView 时出现错误:if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
0赞 Thiha Aung 1/4/2017
表示 self.activeTextField 为 nil
-9赞 2 revs, 2 users 67%Bruce Lee #46

首先,我为您的页面推荐更好的设计,这样就不需要滚动您的视图。如果您有很多文本字段,您仍然不必使用 ScrollView,它只会使事情变得复杂。只需在控制器原始视图的顶部添加容器 UIView,然后将这些文本字段放在该视图上即可。当键盘显示或消失时,只需使用动画来移动此容器视图即可。

3赞 2 revs, 2 users 93%Damien Romito #47

扩展 UIViewController 的简单解决方案

https://github.com/damienromito/VisibleFormViewController

enter image description here

0赞 Anthony Scott #48

我使用 Swift 和自动布局(但无法评论之前的 Swift 答案);这是我如何在没有滚动视图的情况下做到这一点:

我在 IB 中布局我的表单,字段之间有垂直约束以分隔它们。我将从最顶层字段的垂直约束添加到容器视图,并创建一个出口(在下面的代码中为 topSpaceForFormConstraint)。所需要的只是更新这个约束,我在动画块中做了一个漂亮的柔和动作。当然,高度检查是可选的,在这种情况下,我需要只针对最小的屏幕尺寸进行检查。

可以使用任何常用的 textFieldDidBeginEditing 或 keyboardWillShow 方法调用此函数。

func setFormHeight(top: CGFloat)
{
    let height = UIScreen.mainScreen().bounds.size.height

    // restore text input fields for iPhone 4/4s
    if (height < 568) {
        UIView.animateWithDuration(0.2, delay: 0.0, options: nil, animations: {
            self.topSpaceForFormConstraint.constant = top
            self.view.layoutIfNeeded()
            }, completion: nil)
    }

}
3赞 Manesh #49

我发现这是最好的解决方案,请遵循以下代码:

将以下内容附加到约束中。Vertical Space - Bottom Layout Guide - TextField

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

其次,添加键盘通知的观察者。

- (void)observeKeyboard {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

将此添加到您的viewDidLoad

[self observeKeyboard]; 

最后是处理键盘更改的方法。

- (void)keyboardWillShow:(NSNotification *)notification {
//THIS WILL MAKE SURE KEYBOARD DOESNT JUMP WHEN OPENING QUICKTYPE/EMOJI OR OTHER KEYBOARDS.
kbHeight = 0;
height = 0;
self.textViewBottomConst.constant = height;
self.btnViewBottomConst.constant = height;

    NSDictionary *info = [notification userInfo];
    NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];

    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    CGRect keyboardFrame = [kbFrame CGRectValue];

    CGRect finalKeyboardFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];

    int kbHeight = finalKeyboardFrame.size.height;

    int height = kbHeight + self.textViewBottomConst.constant;

    self.textViewBottomConst.constant = height;

    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];
    }];
}

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

    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    self.textViewBottomConst.constant = 10;

    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];
    }];
}
0赞 fede1608 #50

这对我有用:

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()
}
94赞 5 revs, 5 users 55%Satnam Sync #51

对于 Swift 程序员:

这将为你做所有的事情,只需把它们放在你的视图控制器类中,并实现你的视图控制器,并将textField的委托设置为UITextFieldDelegateself

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

实现委托回调方法:

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

对于 Swift 4、4.2、5: 改变

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

self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)

关于此实现的最后一点说明:如果在显示键盘时将另一个视图控制器推送到堆栈上,这将创建一个错误,即视图将返回到其中心框架,但键盘偏移不会重置。例如,键盘是 nameField 的第一个响应者,但随后按下一个按钮,将帮助视图控制器推送到堆栈上。若要修复偏移错误,请确保在离开视图控制器之前调用 nameField.resignFirstResponder(),并确保同时调用 textFieldDidEndEditing 委托方法。我在 viewWillDisappear 方法中执行此操作。

评论

3赞 levibostian 5/11/2016
SwiftLint 不喜欢,所以我把那行改成了self.view.frame = CGRectOffset(self.view.frame, 0, movement)self.view.frame.offsetInPlace(dx: 0, dy: movement)
2赞 Asinox 1/15/2018
Swift 4 将 self.view.frame = CGRectOffset(self.view.frame, 0, movement) 更改为 self.view.frame.offsetBy(dx: 0, dy: movement)
0赞 Joshua Wolff 7/5/2019
仅供参考,要做到这一点,你必须把。self.view.frame = self.view.frame.offsetBy(dx: 0, dy: 移动)
3赞 Cao Huu Loc #52

我把所有东西都包在一个班级里。只需在加载视图控制器时调用以下代码行:

- (void)viewDidLoad {
    [super viewDidLoad];
    KeyboardInsetScrollView *injectView = [[KeyboardInsetScrollView alloc] init];
    [injectView injectToView:self.view withRootView:self.view];
}

以下是示例项目的链接:
https://github.com/caohuuloc/KeyboardInsetScrollView

21赞 3 revs, 2 users 77%Sourabh Sharma #53

试试这个小技巧。

- (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];
}
2赞 Vidhur #54

简单的解决方案和最新的动画 api。将 origin.y 更改为 215,您可以将其自定义为适合您的值。

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    if (self.view.frame.origin.y >= 0) {

        [UIView animateWithDuration:0.5 animations:^{
           self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y-215, self.view.frame.size.width, self.view.frame.size.height);
       }];
   }
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    if (self.view.frame.origin.y < 0) {
        [UIView animateWithDuration:0.5 animations:^{
           self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y+215, self.view.frame.size.width, self.view.frame.size.height);
        }];

    }
}
11赞 swiftBoy #55

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

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

步骤-1

只需在控制器中复制粘贴以下两种方法即可

- (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];
}

步骤-2

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

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

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

步骤-3

灵魂部分来了,只需替换您的文本字段,然后更改 高度,你想向上移动多少。

- (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];
}

参考资料: 好吧,请感谢这个家伙,他分享了这个漂亮的代码片段,干净的解决方案。

希望这会对那里的人有很大的帮助。

评论

0赞 samthui7 1/8/2016
我不认为这是最好的。我认为 @Dheeraj VS 是对的:如果该文本字段位于表格的单元格中,则可以轻松自动地完成(即使 table.scrollable = NO)。注意:桌子的位置和大小必须合理。例如: - 如果表格的 y 位置从视图底部开始计算 100,则 300 高度的键盘将与整个表格重叠。- 如果表格的高度 = 10,并且当键盘出现时,其中的 TextField 必须向上滚动 100 才能显示,则该 TextField 将超出表格的界限。
0赞 Ben G 3/23/2017
@samthui7 Dheeraj 答案仅在您使用 TableViewController 时才有效,而不仅仅是 tableview。它使它成为有时不合适的约束。
2赞 2 revs, 2 users 71%jayesh mardiya #56

请在文本字段委托方法中添加这些行以在iPad中向上滚动。

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

    CGPoint pt;
    CGRect rc = [textField bounds];
    rc = [textField convertRect:rc toView:scrlView];
    pt = rc.origin;
    pt.x = 0;
    pt.y -= 100;

    [scrlView setContentOffset:pt animated:YES];

    scrlView.contentSize = CGSizeMake(scrlView.frame.size.width, button.frame.origin.y+button.frame.size.height + 8 + 370);
}
10赞 2 revs, 2 users 81%Arvind Kumar #57

使用这个第三方,你甚至不需要写一行

https://github.com/hackiftekhar/IQKeyboardManager

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

伙计们真的消除了管理键盘的头痛。

2赞 samthui7 #58

如果该文本字段位于表格的单元格中,则可以轻松自动地完成(即使 table.scrollable = NO)。

  • 注意:桌子的位置和大小必须合理。 例如:
    • 如果表格的 Y 位置从视图底部算起 100,则 300 高度的键盘将与整个表格重叠。
    • 如果 Table 的 height = 10,并且当键盘出现时,其中的 TextField 必须向上滚动 100 才能可见,则该 TextField 将超出表的边界。
2赞 Ratul Sharker #59

非常轻量级的解决方案可能是使用 KeyboardAnimator

项目拿到了示例实现,文档仍在进行中......

适当用法::它有针对 UITextField 和 UITextView 的特定实现

限度::它完全在 objective-c 上,swift 版本即将推出。

7赞 Alvin George #60

斯威夫特 2.0:

添加 UIScrollView 并在其顶部添加 textFields。制作对 VC 的情节提要引用。

@IBOutlet weak var username: UITextField!
@IBOutlet weak var password: UITextField!
@IBOutlet weak var scrollView: UIScrollView!

添加以下方法:UITextFieldDelegate 和 UIScrollViewDelegate。

//MARK:- TEXTFIELD METHODS
    func textFieldShouldReturn(textField: UITextField) -> Bool {

        if(username.returnKeyType == UIReturnKeyType.Default) {
            password.becomeFirstResponder()
        }
        textField.resignFirstResponder()
        return true
    }
    func textFieldDidBeginEditing(textField: UITextField) {

        dispatch_async(dispatch_get_main_queue()) {

            let scrollPoint:CGPoint = CGPointMake(0,textField.frame.origin.y/4)
            self.scrollView!.setContentOffset(scrollPoint, animated: true);
        }
    }
    func textFieldShouldEndEditing(textField: UITextField) -> Bool {

        dispatch_async(dispatch_get_main_queue()) {
          UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true) })
        }
        return true
    }
    override func touchesBegan(touches: Set<UITouch>,
        withEvent event: UIEvent?) {
            self.view.endEditing(true)
    }
    func scrollViewWillBeginDragging(scrollView: UIScrollView) {
        self.scrollView.scrollEnabled =  true

        dispatch_async(dispatch_get_main_queue()) {
            UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true)

            })
        }
    }
2赞 Mahesh reddy #61

这将完美地工作。滚动视图按文本字段位置进行调整。我相信您会感觉良好

static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.25;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 162;
@interface LoginVC ()
{
  CGFloat animatedDistance;
   CGRect viewFrameKey;
}

 //In ViewDidLoad
   viewFrameKey=self.view.frame;



- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGRect textFieldRect =
[self.view.window convertRect:textField.bounds fromView:textField];
CGRect viewRect =
[self.view.window convertRect:self.view.bounds fromView:self.view];
CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator =
midline - viewRect.origin.y
- MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator =
(MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION)
* viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
if (heightFraction < 0.0)
{
    heightFraction = 0.0;
}
else if (heightFraction > 1.0)
{
    heightFraction = 1.0;
}
UIInterfaceOrientation orientation =
[[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait ||
    orientation == UIInterfaceOrientationPortraitUpsideDown)
{
    animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}
else
{
    animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}
CGRect viewFrame = self.view.frame;
viewFrame.origin.y -= animatedDistance;

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];

[self.view setFrame:viewFrame];

[UIView commitAnimations];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrameKey];
[UIView commitAnimations];
}
2赞 andreas #62

我想扩展@sumanthkodi的答案。

正如一些人所说,他的方法在较新的实现中不起作用,因为当您使用约束时,UIView 无法移动。

我按如下方式编辑了代码(并移植到 Swift 2.0),希望它对一些人有所帮助:


1) 参考要向上移动的视图的垂直约束:

@IBOutlet var viewConstraint: NSLayoutConstraint!

请确保在情节提要中引用此变量并具有约束。

2)添加委托并实现监听器。这与之前的实现相同:

class YourViewController: UIViewController, UITextFieldDelegate {

    ...

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

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

    ...

}

3)将动画方法添加到类中。根据需要设置临时约束值。animateTextFieldYourViewController

func animateTextField(textfield: UITextField, up: Bool) {

    let originalConstraint = 50
    let temporaryConstraint = 0
    let movementDuration = 0.3

    let constraint = CGFloat(up ? temporaryConstraint : originalConstraint)

    containerViewConstraint.constant = constraint
    UIView.animateWithDuration(movementDuration) {
        self.view.layoutIfNeeded()
    }

}
6赞 Jorge Ramos #63

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

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

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

此协议为所有符合它的 UIViewController 提供了两个方法,分别对整个视图进行向上和向下动画处理。KeyboardAnimatable

好的,所以如果符合,所有符合的 UIViewController 也符合 。很酷。KeyboardCapableKeyboardAnimatableKeyboardCapableKeyboardAnimatable

让我们看一个符合 的 ,并对键盘事件做出反应:UIViewControllerKeyboardCapable

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

注意:如果要使用自定义动画而不是推送或拉取视图,则必须在协议上定义自定义方法或在函数上执行这些方法。这取决于你。KeyboardAnimatableKeyboardCapable

2赞 2 revsiDev #64

这可以通过以下代码行使用约束简单地实现

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillShow:)
                                                     name:UIKeyboardWillShowNotification
                                                   object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillHide:)
                                                     name:UIKeyboardWillHideNotification
                                                   object:nil];
}

- (void)keyboardWillShow:(NSNotification *)notification {
    [self adjustTextViewByKeyboardState:YES keyboardInfo:[notification userInfo]];
}

- (void)keyboardWillHide:(NSNotification *)notification {
    [self adjustTextViewByKeyboardState:NO keyboardInfo:[notification userInfo]];
}

- (void)viewDidDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super viewDidDisappear:animated];
}

- (void)adjustTextViewByKeyboardState:(BOOL)showKeyboard keyboardInfo:(NSDictionary *)info {
    CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGFloat height = keyboardFrame.size.height;
    self.constraintToAdjust.constant = height;        UIViewAnimationCurve animationCurve = [info[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
    UIViewAnimationOptions animationOptions = UIViewAnimationOptionBeginFromCurrentState;
    if (animationCurve == UIViewAnimationCurveEaseIn) {
        animationOptions |= UIViewAnimationOptionCurveEaseIn;
    }
    else if (animationCurve == UIViewAnimationCurveEaseInOut) {
        animationOptions |= UIViewAnimationOptionCurveEaseInOut;
    }
    else if (animationCurve == UIViewAnimationCurveEaseOut) {
        animationOptions |= UIViewAnimationOptionCurveEaseOut;
    }
    else if (animationCurve == UIViewAnimationCurveLinear) {
        animationOptions |= UIViewAnimationOptionCurveLinear;
    }
    [UIView animateWithDuration:[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue] delay:0 options:animationOptions animations:^{
        [self.view layoutIfNeeded];
    }                completion:nil];
}
4赞 Javier Calatrava Llavería #65

这是独立于器件的偏移计算。获取键盘和文本字段之间的重叠高度:

func keyboardShown(notification: NSNotification) {
    let info  = notification.userInfo!
    let value: AnyObject = info[UIKeyboardFrameEndUserInfoKey]!

    let rawFrame = value.CGRectValue
    let keyboardFrame = view.convertRect(rawFrame, fromView: nil)

    let screenHeight = UIScreen.mainScreen().bounds.size.height;
    let Ylimit = screenHeight - keyboardFrame.size.height
    let textboxOriginInSuperview:CGPoint = self.view.convertPoint(CGPointZero, fromCoordinateSpace: lastTextField!)

    self.keyboardHeight = (textboxOriginInSuperview.y+self.lastTextField!.frame.size.height) - Ylimit

    if(self.keyboardHeight>0){
        self.animateViewMoving(true, moveValue: keyboardHeight!)
    }else{
        keyboardHeight=0
    }
}

keyBoardHeight 是偏移量。

5赞 2 revs, 2 users 84%Mohammad Kamran Usmani #66

这非常简单,只需将以下代码放在您的类中,并在需要时进行自定义。

-(void)textFieldDidBeginEditing:(UITextField *)textField {
     //Show Keyboard
     self.view.frame = CGRectMake(self.view.frame.origin.x,
                              self.view.frame.origin.y-50,
                              self.view.frame.size.width,
                              self.view.frame.size.height);   
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
     // Hide keyboard
     self.view.frame = CGRectMake(self.view.frame.origin.x,
                              self.view.frame.origin.y+50,
                              self.view.frame.size.width,
                              self.view.frame.size.height); 
}
2赞 Sam92 #67

虽然这个线程已经得到了足够的答案,但我想建议一种更简单但更通用的方法,就像苹果一样,考虑到键盘的高度,这在我们在键盘顶部使用自定义工具栏时非常有帮助。 尽管苹果的做法几乎没有问题。

这是我的方法(略微修改苹果的方式)——

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

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
    self.scrollView.contentInset = contentInsets;
    self.scrollView.scrollIndicatorInsets = contentInsets;
}

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    self.scrollView.contentInset = contentInsets;
    self.scrollView.scrollIndicatorInsets = contentInsets;
}
2赞 Alvin George #68
  1. 下载 TPKeyBoardAvoid 从此链接: https://github.com/michaeltyson/TPKeyboardAvoiding
  2. 展开压缩文件夹并找到 TPKeyboardAvoid 文件夹。
  3. 选择所有 .h 和 .m 文件并将其拖放到您的项目中。确保勾选“如果需要复制项目”。
  4. 将 UIScrollView 拖放到 StoryBoard 并与 TPKeyboardAvoidingScrollView 关联。
  5. 现在,您可以在滚动视图的顶部添加 UI 元素。请注意,即使在拖动 scrollView 后,此类也会检测 UI 元素的触摸。

在 ViewController 上:

@IBOutlet weak var usernameTextfield: UITextField!
@IBOutlet weak var passwordTextfield: UITextField!
@IBOutlet weak var loginScrollView: UIScrollView!


override func viewWillAppear(animated: Bool) {
        loginScrollView.scrollEnabled =  false
    }

添加 TextField 委托。

//MARK:- TEXTFIELD METHODS
func textFieldShouldReturn(textField: UITextField) -> Bool
{
    if (usernameTextfield.resignFirstResponder())
    {
        passwordTextfield.becomeFirstResponder()
    }
    textField.resignFirstResponder();
    loginScrollView!.setContentOffset(CGPoint.zero, animated: true);
    loginScrollView.scrollEnabled =  false
    return true
}
func textFieldDidBeginEditing(textField: UITextField)
{
    loginScrollView.scrollEnabled =  true

    if (textField.tag  == 1 && (device == "iPhone" || device == "iPhone Simulator" || device == "iPod touch"))
    {
        let scrollPoint:CGPoint = CGPointMake(0, passwordTextfield.frame.origin.y/6.4);
        loginScrollView!.setContentOffset(scrollPoint, animated: true);

    }
    else if (textField.tag  == 2 && (device == "iPhone" || device == "iPhone Simulator" || device == "iPod touch"))
    {
        let scrollPoint:CGPoint = CGPointMake(0, passwordTextfield.frame.origin.y/6.0);
        loginScrollView!.setContentOffset(scrollPoint, animated: true);
    }
}
func textFieldDidEndEditing(textField: UITextField)
{
    loginScrollView!.setContentOffset(CGPointZero,animated: true);
}
2赞 TPG #69

这段代码将根据键盘高度和文本字段消失的深度来计算需要向上移动的程度。请记住在标头中添加委托并继承 UITextFieldDelegate。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [_tbxUsername resignFirstResponder];
    [_tbxPassword resignFirstResponder];
}

- (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
{
    int animatedDistance;
    int moveUpValue = textField.frame.origin.y+ textField.frame.size.height;
    UIInterfaceOrientation orientation =
    [[UIApplication sharedApplication] statusBarOrientation];
    if (orientation == UIInterfaceOrientationPortrait ||
        orientation == UIInterfaceOrientationPortraitUpsideDown)
    {

        animatedDistance = 236-(460-moveUpValue-5);
    }
    else
    {
        animatedDistance = 182-(320-moveUpValue-5);
    }

    if(animatedDistance>0)
    {
        const int movementDistance = animatedDistance;
        const float movementDuration = 0.3f;
        int movement = (up ? -movementDistance : movementDistance);
        [UIView beginAnimations: nil context: nil];
        [UIView setAnimationBeginsFromCurrentState: YES];
        [UIView setAnimationDuration: movementDuration];
        self.view.frame = CGRectOffset(self.view.frame, 0, movement);
        [UIView commitAnimations];
    }
}

要在 ViewDidLoad 添加的委托

_tbxUsername.delegate = self;
_tbxPassword.delegate = self;
4赞 Sam92 #70

一种更简单但更通用的方法,就像苹果所做的那样,它考虑了键盘的高度,这在我们在键盘顶部使用自定义工具栏时非常有帮助。 尽管苹果的做法几乎没有问题。

这是我的方法(略微修改苹果的方式)——

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

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
    self.scrollView.contentInset = contentInsets;
    self.scrollView.scrollIndicatorInsets = contentInsets;
}

// UIKeyboardWillHideNotification
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    self.scrollView.contentInset = contentInsets;
    self.scrollView.scrollIndicatorInsets = contentInsets;
}

评论

0赞 Leo 12/30/2019
这就是本质!我在这里尝试设置contentOffset或scrollRectToVisible unnec的许多示例。UITextField 将自身滚动到 .可能会阻止这种情况的因素是 UIScrollView 的模棱两可的内容大小或对滚动到视图方法之一的显式调用。在 github.com/leopatras/ios_textfields_on_scrollview 上查看使用这种简单方法的工作演示。Apples 文档令人困惑,因为它没有提到 UITextField 根本不需要它。[UITextField becomeFirstResponder]scrollRectToView
3赞 2 revs, 2 users 86%seggy #71

在视图中设置 Scrollview

  - (void)textFieldDidBeginEditing:(UITextField *)textField
    {
     CGPoint point;
    if(textField == txtEmail){
      // -90 is for my you can change as per your postion
      point = CGPointMake(0, textField.frame.origin.y - 90);
    }
    else if (textField == txtContact){
      point = CGPointMake(0, textField.frame.origin.y - 90);
    }
      [scrollV setContentOffset:point animated:YES];
    }
1赞 tryKuldeepTanwar #72

它很简单:-

在 TextFieldDidBeginEditing 中:-

self.view.frame=CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y-150, self.view.frame.size.width, self.view.frame.size.height);

在 TextFieldShouldEndEditing 中:-

self.view.frame=CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y+150, self.view.frame.size.width, self.view.frame.size.height);

评论

0赞 Alp Altunel 2/24/2018
这在显示为弹出视图的 XIB 文件上完美运行
0赞 Esha #73

在 iOS 中,向上移动键盘并收回应用程序中的文本字段有点令人困惑,并且需要实现一些相同的方法。 此外,您还需要委托给 TextField 并处理它。 它的代码将在文本字段存在的每个类中重复。

我更喜欢使用这个 Github 控件。

IQ凯板

其中 我们不需要做任何事情。-- 只需将 DROP 控件拖动到您的项目并构建即可。-- 它将为您的应用程序做所有事情。

谢谢

也许这将是有用的。

2赞 User511 #74

试试 IQKeyboard 库。

这将自动向上移动文本字段。

0赞 Robert Juz #75

在更改文本字段或编辑其内容时,我遇到了重置为默认主视图的问题(例如,手机文本字段并添加“-”符号,并且视图返回覆盖文本字段) 我最终通过使用自动布局和更改约束常量来克服这个问题,而不是通知委托函数中的帧大小或位置,如下所示:

PS 我没有使用滚动视图只是简单的向上移动视图,但它应该类似地工作

func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
    if !keyboardIsShown{
        self.infoViewTopConstraint.constant -= keyboardSize.height
        self.infoViewBottomConstraint.constant += keyboardSize.height
        self.view.setNeedsLayout()
        self.view.layoutIfNeeded()
        keyboardIsShown = true
    }
}

func keyboardWillHide(notification: NSNotification) {
if keyboardIsShown {
    self.infoViewTopConstraint.constant += keyboardSize.height
    self.infoViewBottomConstraint.constant -= keyboardSize.height
    self.view.setNeedsLayout()
    self.view.layoutIfNeeded()
    keyboardIsShown = false
}
-3赞 HariKarthick #76

试试这个,它工作得很好:

if Firstnametxt.text == "" || Passwordtxt.text == "" || emailtxt.text == ""
    {
        if Firstnametxt.text == ""
        {
            Firstnametxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
            Firstnametxt.becomeFirstResponder()
        }
        else if Passwordtxt.text == ""
        {
            Passwordtxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
            Passwordtxt.becomeFirstResponder()
        }
        else if emailtxt.text == ""

        {

            emailtxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
            emailtxt.becomeFirstResponder()
        }

    }
    else
    {
        let isValidEmail:Bool = self.isValidEmail(emailtxt.text!)
        if isValidEmail == true
        {
                        }
        else
        {
            emailtxt!.shake(10, withDelta: 5, speed: 0.05, shakeDirection: ShakeDirection.Horizontal)
            emailtxt.becomeFirstResponder()

        }

    }

评论

0赞 EJoshuaS - Stand with Ukraine 1/10/2017
最好在代码中包含一些上下文/解释,因为这会使答案对 OP 和未来的读者更有用。
1赞 Emre Gürses #77

我迟到了一点。您应该在 viewController 上添加 scrollView。

您必须实现以下 2 种方法。

TextField 委托方法。

    - (void)textFieldDidBeginEditing:(UIView *)textField {
    [self scrollViewForTextField:reEnterPINTextField];
}

然后在委托方法中调用下面的 methon。

 - (void)scrollViewForTextField:(UIView *)textField {
    NSInteger keyboardHeight = KEYBOARD_HEIGHT;

    if ([textField UITextField.class]) {
        keyboardHeight += ((UITextField *)textField).keyboardControl.activeField.inputAccessoryView.frame.size.height;
    } 

    CGRect screenFrame = [UIScreen mainScreen].bounds;
    CGRect aRect = (CGRect){0, 0, screenFrame.size.width, screenFrame.size.height - ([UIApplication sharedApplication].statusBarHidden ? 0 : [UIApplication sharedApplication].statusBarFrame.size.height)};
    aRect.size.height -= keyboardHeight;
    CGPoint relativeOrigin = [UIView getOriginRelativeToScreenBounds:textField];
    CGPoint bottomPointOfTextField = CGPointMake(relativeOrigin.x, relativeOrigin.y + textField.frame.size.height);

    if (!CGRectContainsPoint(aRect, bottomPointOfTextField) ) {
        CGPoint scrollPoint = CGPointMake(0.0, bottomPointOfTextField.y -aRect.size.height);
        [contentSlidingView setContentOffset:scrollPoint animated:YES];
    }
}
2赞 abhimuralidharan #78

Swift 3.0 版本的 Apple 键盘管理代码在这里: 以下代码中使用的 FloatingTF 是 iOS 中基于 Material 设计的文本字段。

import UIKit
class SignupViewController: UIViewController, UITextFieldDelegate {

    //MARK: - IBOutlet:
@IBOutlet weak var emailTF: FloatingTF!
@IBOutlet weak var passwordTF: FloatingTF!
@IBOutlet weak var dobTF: FloatingTF!

@IBOutlet weak var scrollView: UIScrollView!

//MARK: - Variable:
var activeTextField: UITextField!

//MARK: - ViewController Lifecycle:
override func viewDidLoad() {
    super.viewDidLoad()        
    emailTF.delegate = self
    passwordTF.delegate = self
    dobTF.delegate = self 
}
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    registerKeyboardNotifications()
}
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

   deRegisterKeyboardNotifications()
}

//MARK: - Keyboard notification observer Methods
fileprivate func registerKeyboardNotifications() {
    NotificationCenter.default.addObserver(self, selector: #selector(SignupViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(SignupViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
fileprivate func deRegisterKeyboardNotifications() {
    NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: self.view.window)
    NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidHide, object: self.view.window)
}
func keyboardWillShow(notification: NSNotification) {

    let info: NSDictionary = notification.userInfo! as NSDictionary
    let value: NSValue = info.value(forKey: UIKeyboardFrameBeginUserInfoKey) as! NSValue
    let keyboardSize: CGSize = value.cgRectValue.size
    let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0)
    scrollView.contentInset = contentInsets
    scrollView.scrollIndicatorInsets = contentInsets

    // If active text field is hidden by keyboard, scroll it so it's visible
    // Your app might not need or want this behavior.
    var aRect: CGRect = self.view.frame
    aRect.size.height -= keyboardSize.height
    let activeTextFieldRect: CGRect? = activeTextField?.frame
    let activeTextFieldOrigin: CGPoint? = activeTextFieldRect?.origin
    if (!aRect.contains(activeTextFieldOrigin!)) {
        scrollView.scrollRectToVisible(activeTextFieldRect!, animated:true)
    }    }

func keyboardWillHide(notification: NSNotification) {
    let contentInsets: UIEdgeInsets = .zero
    scrollView.contentInset = contentInsets
    scrollView.scrollIndicatorInsets = contentInsets
}

//MARK: - UITextField Delegate Methods
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    if textField == emailTF {
        passwordTF.becomeFirstResponder()
    }
    else if textField == passwordTF {
        dobTF.becomeFirstResponder()
    }
    else {
        self.view.endEditing(true)
    }
    return true
}

func textFieldDidBeginEditing(_ textField: UITextField) {
    activeTextField = textField
    scrollView.isScrollEnabled = true
}

func textFieldDidEndEditing(_ textField: UITextField) {
    activeTextField = nil
    scrollView.isScrollEnabled = false
}
}
6赞 Kakshil Shah #79

只需将其添加到您的 pod 文件 ->pod 'IQKeyboardManager'

就是这样,处理所有的键盘、滚动视图和一切!

您不需要编写任何代码,找不到更好的解决方案!

它有一个扩展,如果有多个文本字段,它可以处理文本字段显示、屏幕移动、下一个和上一个箭头。

它还有一个自定义的完成按钮,可以将其删除。

链接 -> https://github.com/hackiftekhar/IQKeyboardManager

0赞 James Rochabrun #80

对于使用 Swift 3 的 Swift 开发人员,这里是 repo https://github.com/jamesrochabrun/KeyboardWillShow

import UIKit

class ViewController: UIViewController {

    //1 Create a view that will hold your TEXTFIELD
    let textField: UITextField = {
        let tf = UITextField()
        tf.translatesAutoresizingMaskIntoConstraints = false
        tf.layer.borderColor = UIColor.darkGray.cgColor
        tf.layer.borderWidth = 3.0
        return tf
    }()
    //2 global variable that will hold the bottom constraint on changes
    var textfieldBottomAnchor: NSLayoutConstraint?

    override func viewDidLoad() {
        super.viewDidLoad()
        //3 add the view to your controller
        view.addSubview(textField)
        textField.heightAnchor.constraint(equalToConstant: 80).isActive = true
        textField.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
        textField.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        textfieldBottomAnchor = textField.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        textfieldBottomAnchor?.isActive = true

        setUpKeyBoardObservers()
    }
    //4 Use NSnotificationCenter to monitor the keyboard updates
    func setUpKeyBoardObservers() {
        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    //5 toggle the bottom layout global variable based on the keyboard's height
    func handleKeyboardWillShow(notification: NSNotification) {

        let keyboardFrame = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? CGRect
        if let keyboardFrame = keyboardFrame {
            textfieldBottomAnchor?.constant = -keyboardFrame.height
        }
        let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
        if let keyboardDuration = keyboardDuration {
            UIView.animate(withDuration: keyboardDuration, animations: {
                self.view.layoutIfNeeded()
            })
        }
    }

    func handleKeyboardWillHide(notification: NSNotification) {

        textfieldBottomAnchor?.constant = 0
        let keyboardDuration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? Double
        if let keyboardDuration = keyboardDuration {
            UIView.animate(withDuration: keyboardDuration, animations: {
                self.view.layoutIfNeeded()
            })
        }
    }
    //6 remove the observers
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        NotificationCenter.default.removeObserver(self)
    }
}
2赞 Jaydeep Vyas #81

带有文本字段的滚动视图的简单解决方案如下,不需要任何约束或活动文本字段等...

 override func viewWillAppear(_ animated: Bool){
        super.viewWillAppear(animated)
        registerForKeyboardNotifications();


    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        deregisterFromKeyboardNotifications();
    }
    //MARK:- KEYBOARD DELEGATE METHODS
        func registerForKeyboardNotifications(){
            //Adding notifies on keyboard appearing
            NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        }
        func deregisterFromKeyboardNotifications(){
            //Removing notifies on keyboard appearing
            NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
            NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        }
        func keyboardWasShown(notification: NSNotification){

            var info = notification.userInfo!
            let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
            var contentInset:UIEdgeInsets = self.scrRegister.contentInset
            contentInset.bottom = (keyboardSize?.height)!
            scrRegister.contentInset = contentInset


        }
        func keyboardWillBeHidden(notification: NSNotification)
        {
            var contentInset:UIEdgeInsets = self.scrRegister.contentInset
            contentInset.bottom = 0
            scrRegister.contentInset = contentInset

        }
4赞 Badr #82

我知道为时已晚,但我想与未来的访客分享,尤其是我的做法。分享了许多好的方法,但我不喜欢 UI 变得完全糟糕。有一个简单的方法,包括两部分:-

  1. 将 TextField 以及您希望在编辑时浮动在键盘上方的任何内容添加到视图中,以便它们成为视图的子项。然后,将很容易保持外观并且不会严重影响UI。
  2. 使用出色的工具将创建的视图移动到键盘上方。CGAffineTransform(TranslationX: x, TranslationY: y)

我知道这看起来很简单,但它真的很有效和整洁。enter image description here

0赞 3 revs, 3 users 72%Harminder Singh #83

您可以使用以下简单的 Git 存储库:https://github.com/hackiftekhar/IQKeyboardManager

这是一个自动管理所有字段移动的库。

根据他们的自述文件,集成非常简单:

无需您输入任何代码,也无需额外设置。要使用 IQKeyboardManager,您只需将源文件添加到您的项目中

虽然,这是非常好的控制,但在某些情况下它会导致冲突,例如在具有滚动视图的视图控制器中。它有时会更改内容大小。不过,你可以去做,并按照你的要求尝试,也许你可以做我错过的事情。

14赞 3 revs, 2 users 98%ZAFAR007 #84

斯威夫特 4 .

您可以使用带有动画的 UIKeyBoard 轻松上下移动 UITextFieldUIView enter image description here

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
    }

}

评论

2赞 Houman 8/19/2018
近乎完美。不过,在iPhone X上,键盘和文本字段之间有一个奇怪的间隙。
0赞 JIE WANG #85

这个问题已经有很多答案了,有的说用滚动视图,有的说用第三个库。

但对我来说,解决方案应该是静态单元。UITableViewController

你把你的UI分成多个部分,然后把它们一个一个地放在tableViewCells中,你就不用再担心键盘了,tableViewController会自动为你管理它。

计算填充、边距、单元格高度可能有点困难,但如果你的数学没问题,那就很简单了。

-2赞 3 revs, 2 users 52%dmani ms #86

参考以下内容

import UIKit
@available(tvOS, unavailable)
public class KeyboardLayoutConstraint: NSLayoutConstraint {

    private var offset : CGFloat = 0
    private var keyboardVisibleHeight : CGFloat = 0

    @available(tvOS, unavailable)
    override public func awakeFromNib() {
        super.awakeFromNib()

        offset = constant

        NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillShowNotification(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(KeyboardLayoutConstraint.keyboardWillHideNotification(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    // MARK: Notification

    @objc func keyboardWillShowNotification(_ notification: Notification) {
        if let userInfo = notification.userInfo {
            if let frameValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue {
                let frame = frameValue.cgRectValue
                keyboardVisibleHeight = frame.size.height
            }

            self.updateConstant()
            switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) {
            case let (.some(duration), .some(curve)):

                let options = UIViewAnimationOptions(rawValue: curve.uintValue)

                UIView.animate(
                    withDuration: TimeInterval(duration.doubleValue),
                    delay: 0,
                    options: options,
                    animations: {
                        UIApplication.shared.keyWindow?.layoutIfNeeded()
                        return
                    }, completion: { finished in
                })
            default:

                break
            }

        }

    }

    @objc func keyboardWillHideNotification(_ notification: NSNotification) {
        keyboardVisibleHeight = 0
        self.updateConstant()

        if let userInfo = notification.userInfo {

            switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) {
            case let (.some(duration), .some(curve)):

                let options = UIViewAnimationOptions(rawValue: curve.uintValue)

                UIView.animate(
                    withDuration: TimeInterval(duration.doubleValue),
                    delay: 0,
                    options: options,
                    animations: {
                        UIApplication.shared.keyWindow?.layoutIfNeeded()
                        return
                    }, completion: { finished in
                })
            default:
                break
            }
        }
    }

    func updateConstant() {
        self.constant = offset + keyboardVisibleHeight
    }

}
0赞 Pankaj Jangid #87

我们可以为用户给定 Swift 4.1 的代码

    let keyBoardSize = 80.0

    func keyboardWillShow() {

    if view.frame.origin.y >= 0 {
    viewMovedUp = true
     }
     else if view.frame.origin.y < 0 {
    viewMovedUp = false
   }
  }

func keyboardWillHide() {
 if view.frame.origin.y >= 0 {
    viewMovedUp = true
 }
 else if view.frame.origin.y < 0 {
    viewMovedUp = false
 }

}

func textFieldDidBeginEditing(_ textField: UITextField) {
   if sender.isEqual(mailTf) {
    //move the main view, so that the keyboard does not hide it.
    if view.frame.origin.y >= 0 {
        viewMovedUp = true
    }
  }
}

func setViewMovedUp(_ movedUp: Bool) {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.3)
    // if you want to slide up the view
let rect: CGRect = view.frame
if movedUp {

    rect.origin.y -= keyBoardSize
    rect.size.height += keyBoardSize
}
else {
    // revert back to the normal state.
    rect.origin.y += keyBoardSize
    rect.size.height -= keyBoardSize
 }
 view.frame = rect
 UIView.commitAnimations()
}

func viewWillAppear(_ animated: Bool)  {
super.viewWillAppear(animated)

NotificationCenter.default.addObserver(self, selector:#selector(self.keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(self.keyboardWillHide), name: .UIKeyboardWillHide, object: nil)
}

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

NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
0赞 hbk #88

添加我的 5 美分:)

我总是喜欢将 tableView 用于 inputTextField 或 scrollView。结合通知,您可以轻松管理此类行为。(请注意,如果您在 tableView 中使用静态单元格,则此类行为将自动为您管理。

// MARK: - Notifications
fileprivate func registerNotificaitions() {
    NotificationCenter.default.addObserver(self, selector: #selector(AddRemoteControlViewController.keyboardWillAppear(_:)),
                                           name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(AddRemoteControlViewController.keyboardWillDisappear),
                                           name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

fileprivate func unregisterNotifications() {
    NotificationCenter.default.removeObserver(self)
}

@objc fileprivate func keyboardWillAppear(_ notification: Notification) {
    if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
        view.layoutIfNeeded()
        UIView.animate(withDuration: 0.3, animations: {
            let heightInset = keyboardHeight - self.addDeviceButton.frame.height
            self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: heightInset, right: 0)
            self.view.layoutIfNeeded()
        }, completion: nil)
    }
}

@objc fileprivate func keyboardWillDisappear() {
    view.layoutIfNeeded()
    UIView.animate(withDuration: 0.3, animations: {
        self.tableView.contentInset = UIEdgeInsets.zero
        self.view.layoutIfNeeded()
    }, completion: nil)
}
0赞 Sergiu Todirascu #89

如果文本字段应该在屏幕底部,那么最神奇的解决方案是对视图控制器进行以下覆盖:

override var inputAccessoryView: UIView? {
    return <yourTextField>
}
2赞 naresh kolindala #90

使用 IQKeyboardManager, 当键盘出现时,UITextField 和 UITextView 会自动滚动。 Git 链接:https://github.com/hackiftekhar/IQKeyboardManager

荚: pod 'IQKeyboardManager' #iOS8 及更高版本

pod 'IQKeyboardManager', '3.3.7' #iOS7

2赞 Паша Матюхин #91

斯威夫特 5

在 viewDidLoad 或 viewDidAppear 中添加 addKeyboardObservers 方法。

fileprivate func addKeyboardObservers(){
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

fileprivate func removeKeyboardObservers(){
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
}


@objc fileprivate func keyboardWillHide(_ notification: Notification){
    if (window == nil) {return}
    guard let duration = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double) else {return}

   scrollView.contentInset.bottom = .zero
}


@objc fileprivate func keyboardWillShow(_ notification: Notification){
    if (window == nil) {return}
    if UIApplication.shared.applicationState != .active { return }

    // keyboard height
    guard let height = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height else {return}
    // keyboard present animation duration
    guard let duration = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double) else {return}

   scrollView.contentInset.bottom = height
}

不要忘记删除 Deinit 上的观察者或消失

    self.removeKeyboardObservers()

评论

1赞 Jay Lee 7/26/2019
关于 addObserver:selector:name:object: 的 Apple 文档显示“如果您的应用面向 iOS 9.0 及更高版本或 macOS 10.11 及更高版本,则无需在其 dealloc 方法中取消注册观察者。
3赞 2 revsMojtaba Hosseini #92

- SwiftUI的

仅显示活动状态TextField

这将使视图移动到足以避免仅隐藏活动 TextField 的程度。When each TextField is clicked, the view is only moved up enough to make the clicked text field visible.

struct ContentView: View {
    @ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 3)
    @State private var name = Array<String>.init(repeating: "", count: 3)

    var body: some View {

        VStack {
            Group {
                Text("Some filler text").font(.largeTitle)
                Text("Some filler text").font(.largeTitle)
            }

            TextField("text #1", text: $name[0], onEditingChanged: { if $0 { self.kGuardian.showField = 0 } })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[0]))

            TextField("text #2", text: $name[1], onEditingChanged: { if $0 { self.kGuardian.showField = 1 } })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[1]))

            TextField("text #3", text: $name[2], onEditingChanged: { if $0 { self.kGuardian.showField = 2 } })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[2]))

            }.offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.25))
    }

}

显示全部TextField

这会将所有 textField 向上移动(如果其中任何一个出现键盘)。但仅在需要时。如果键盘不隐藏文本字段,它们将不会移动。When the keyboard is opened, the 3 textfields are moved up enough to keep then all visible

struct ContentView: View {
    @ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 1)
    @State private var name = Array<String>.init(repeating: "", count: 3)

    var body: some View {

        VStack {
            Group {
                Text("Some filler text").font(.largeTitle)
                Text("Some filler text").font(.largeTitle)
            }

            TextField("enter text #1", text: $name[0])
                .textFieldStyle(RoundedBorderTextFieldStyle())

            TextField("enter text #2", text: $name[1])
                .textFieldStyle(RoundedBorderTextFieldStyle())

            TextField("enter text #3", text: $name[2])
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[0]))

        }.offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.25))
    }

}

这两个示例都使用相同的通用代码:GeometryGetterKeyboardGuardian Inspired by @kontiki

GeometryGetter

这是一个吸收其父视图的大小和位置的视图。在此处封装描述为了实现这一点,它在 .background 修饰符中被调用。这是一个非常强大的修饰符,而不仅仅是一种装饰视图背景的方法。将视图传递给 .background(MyView()) 时,MyView 将获取修改后的视图作为父视图。使用 GeometryReader 可以使视图知道父项的几何图形。

例如:将使用文本视图的大小和位置以及使用全局坐标空间填充可变边界。Text("hello").background(GeometryGetter(rect: $bounds))

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { geometry in
            Group { () -> AnyView in
                DispatchQueue.main.async {
                    self.rect = geometry.frame(in: .global)
                }

                return AnyView(Color.clear)
            }
        }
    }
}

请注意,这是为了避免在呈现视图时修改视图状态的可能性。DispatchQueue.main.async

键盘守护者

KeyboardGuardian 的目的是跟踪键盘显示/隐藏事件,并计算需要移动视图的空间。

请注意,当用户从一个字段切换到另一个字段时,它会刷新幻灯片*

import SwiftUI
import Combine

final class KeyboardGuardian: ObservableObject {
    public var rects: Array<CGRect>
    public var keyboardRect: CGRect = CGRect()

    // keyboardWillShow notification may be posted repeatedly,
    // this flag makes sure we only act once per keyboard appearance
    public var keyboardIsHidden = true

    @Published var slide: CGFloat = 0

    var showField: Int = 0 {
        didSet {
            updateSlide()
        }
    }

    init(textFieldCount: Int) {
        self.rects = Array<CGRect>(repeating: CGRect(), count: textFieldCount)

        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)

    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @objc func keyBoardWillShow(notification: Notification) {
        if keyboardIsHidden {
            keyboardIsHidden = false
            if let rect = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect {
                keyboardRect = rect
                updateSlide()
            }
        }
    }

    @objc func keyBoardDidHide(notification: Notification) {
        keyboardIsHidden = true
        updateSlide()
    }

    func updateSlide() {
        if keyboardIsHidden {
            slide = 0
        } else {
            let tfRect = self.rects[self.showField]
            let diff = keyboardRect.minY - tfRect.maxY

            if diff > 0 {
                slide += diff
            } else {
                slide += min(diff, 0)
            }

        }
    }
}
-1赞 Vasucd #93

如果您希望 UIView 正确移位,并且您的活动文本字段应根据用户需求准确定位,以便他/她可以看到他们输入的任何内容。

为此,您必须使用 Scrollview 。 这假定是 UIView 层次结构。 ContainerView -> ScrollView -> ContentView -> 您的视图。

如果您已经按照上述讨论层次结构进行了 UIView 设计,那么现在在控制器类中,您需要在 viewwillappear 中添加通知观察者,并在 viewwilldissappear 中删除观察者。

但是这种方法需要在每个控制器上添加 UIView 需要转移的地方。 我一直在使用“TPKeyboardAvoiding”pod。它是可靠的,并且可以轻松处理UIView的移位,适用于每种可能的情况,无论是Scrollview,TableView还是CollectionView。你只需要将类传递给你的“滚动视图”。

如下图所示

demo Image

如果将 tableview 更改为“TPKeyboardAvoidingTableView”,则可以将此类更改为“TPKeyboardAvoidingTableView”。 您可以找到完整的正在运行的项目项目链接

我在开发中遵循了这种强大的方法。希望这有帮助!

-1赞 3 revs, 2 users 64%Nicolás A. Rodríguez #94

使用 ViewModifier 的 SwiftUI

您可以使用 SwiftUI。这要简单得多。ViewModifier

import SwiftUI
import Combine

struct KeyboardAwareModifier: ViewModifier {
    @State private var keyboardHeight: CGFloat = 0

    private var keyboardHeightPublisher: AnyPublisher<CGFloat, Never> {
        Publishers.Merge(
            NotificationCenter.default
                .publisher(for: UIResponder.keyboardWillShowNotification)
                .compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue }
                .map { $0.cgRectValue.height },
            NotificationCenter.default
                .publisher(for: UIResponder.keyboardWillHideNotification)
                .map { _ in CGFloat(0) }
       ).eraseToAnyPublisher()
    }

    func body(content: Content) -> some View {
        content
            .padding(.bottom, keyboardHeight)
            .onReceive(keyboardHeightPublisher) { self.keyboardHeight = $0 }
    }
}

extension View {
    func KeyboardAwarePadding() -> some View {
        ModifiedContent(content: self, modifier: KeyboardAwareModifier())
    }
}

在你看来

struct SomeView: View {
    @State private var someText: String = ""

    var body: some View {
        VStack {
            Spacer()
            TextField("some text", text: $someText)
        }.KeyboardAwarePadding()
    }
}

KeyboardAwarePadding()将自动向视图添加填充。它更优雅。

1赞 4 revsOrçun Deniz #95

我已经为我自己的需求开发了一个框架,以更好地解决这个问题,现在将其公开。它不仅适用于 UITextField 和 UITextView,它还适用于任何采用 UITextInput 协议(如 UITextField 和 UITextView)并提供许多有用功能的自定义 UIView。您可以通过 Carthage、CocoaPods 或 Swift Package Manager 安装它。

ODScrollView GitHub上

ODScrollView 中型

ODScrollView 只是一个 UIScrollView,它根据键盘可见性自动垂直移动可编辑的文本区域,如 UITextField 和 UITextView,以提供更好的用户体验。

特征

  • 当键盘出现/消失时,自动向上/向下移动采用 UITextInput 协议的急救人员 UIView,例如 UITextField、UITextView、UISearchTextField 或任何采用 UITextInput 协议的自定义 UIView。
    • 请注意,如果 UITextInput 的框架不适合 ODScrollView 和键盘之间的剩余区域,则 ODScrollView 会根据光标位置而不是框架调整 UITextInput。在这种情况下,可以使用“trackTextInputCursor”功能。
  • 可以对 的每个 UITextInput 单独应用调整边距。top 和 .底部调整方向设置。默认为 20 CGFloat。

  • 可以单独启用/禁用每个 UITextInput 的调整。默认值为 true。

  • 调整指示 - .返回页首。中心。底部 - 可以单独应用于每个 UITextInput。.默认为底部。

  • 调整选项确定 ODScrollView 的调整方式。.始终默认。
    • .始终:ODScrollView 始终调整放置在 ODScrollView 中任何位置的 UITextInput,无论 UITextInput 是否与显示的键盘重叠。
    • .IfNeeded:ODScrollView 仅在 UITextInput 与显示的键盘重叠时调整它。
  • 除了 UIScrollView.keyboardDismissModes 之外,还可以通过点击 ODScrollViewDelegate 提供的 UIView 来关闭键盘。关闭键盘后,ODScrollView 可以返回其原始位置。默认情况下为 nil 和 false。

用法

1 -您需要做的第一件事是正确设置 ODScrollView 及其内容视图。由于 ODScrollView 只是一个 UIScrollView,因此可以采用与 UIScrollView 相同的方式实现 ODScrollView。您可以使用情节提要或以编程方式创建 ODScrollView。

如果以编程方式创建 ODScrollView,则可以从步骤 4 继续。

在 Storyboard 中创建 UIScrollView 的建议方法

- If you are using Content Layout Guide and Frame Layout Guide:
    1.1 - scrollView: Place UIScrollView anywhere you want to use.  
    1.2 - contentView: Place UIView inside scrollView.
    1.3 - Set contentView's top, bottom, leading and trailing constraints to Content Layout Guide's constraints.
    1.4 - Set contentView's width equal to Frame Layout Guide's width.
    1.5 - Set contentView's height equal to Frame Layout Guide's height or set static height which is larger than scrollView's height.
    1.6 - Build your UI inside contentView.

- If you are NOT using Content Layout Guide and Frame Layout Guide:
    1.1 - scrollView: Place UIScrollView anywhere you want to use.  
    1.2 - contentView: Place UIView inside scrollView.
    1.3 - Set contentView's top, bottom, leading and trailing constraints to 0.
    1.4 - Set contentView's width equal to scrollView's width.
    1.5 - Set contentView's height equal to scrollView's superview's height or set static height which is larger than scrollView's height.
    1.6 - Build your UI inside contentView.

阿拉伯数字-在 Storyboard 上的标识检查器中,将 scrollView 的类从 UIScrollView 更改为 ODScrollView。

3 -在 ViewController 上为 scrollView 和 contentView 创建 IBOutlets。

4 -在 ViewController 上的 ViewDidLoad() 中调用以下方法:

override func viewDidLoad() {
    super.viewDidLoad()

    //ODScrollView setup
    scrollView.registerContentView(contentView)
    scrollView.odScrollViewDelegate = self
}  

5 -可选:您仍然可以使用 UIScrollView 的功能:

override func viewDidLoad() {
    super.viewDidLoad()

    //ODScrollView setup
    scrollView.registerContentView(contentView)
    scrollView.odScrollViewDelegate = self

    // UIScrollView setup
    scrollView.delegate = self // UIScrollView Delegate
    scrollView.keyboardDismissMode = .onDrag // UIScrollView keyboardDismissMode. Default is .none.

    UITextView_inside_contentView.delegate = self
}

6 -采用 ViewController 中的 ODScrollViewDelegate 并确定 ODScrollView 选项:

extension ViewController: ODScrollViewDelegate {

    // MARK:- State Notifiers: are responsible for notifiying ViewController about what is going on while adjusting. You don't have to do anything if you don't need them.

    // #Optional
    // Notifies when the keyboard showed.
    func keyboardDidShow(by scrollView: ODScrollView) {}

    // #Optional
    // Notifies before the UIScrollView adjustment.
    func scrollAdjustmentWillBegin(by scrollView: ODScrollView) {}

    // #Optional
    // Notifies after the UIScrollView adjustment.
    func scrollAdjustmentDidEnd(by scrollView: ODScrollView) {}

    // #Optional
    // Notifies when the keyboard hid.
    func keyboardDidHide(by scrollView: ODScrollView) {}

    // MARK:- Adjustment Settings

    // #Optional
    // Specifies the margin between UITextInput and ODScrollView's top or bottom constraint depending on AdjustmentDirection
    func adjustmentMargin(for textInput: UITextInput, inside scrollView: ODScrollView) -> CGFloat {
        if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
            return 20
        } else {
            return 40
        }
    }

    // #Optional
    // Specifies that whether adjustment is enabled or not for each UITextInput seperately.
    func adjustmentEnabled(for textInput: UITextInput, inside scrollView: ODScrollView) -> Bool {
        if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
            return true
        } else {
            return false
        }
    }


    // Specifies adjustment direction for each UITextInput. It means that  some of UITextInputs inside ODScrollView can be adjusted to the bottom, while others can be adjusted to center or top.
    func adjustmentDirection(selected textInput: UITextInput, inside scrollView: ODScrollView) -> AdjustmentDirection {
        if let textField = textInput as? UITextField, textField == self.UITextField_inside_contentView {
            return .bottom
        } else {
            return .center
        }
    }

    /**
     - Always : ODScrollView always adjusts the UITextInput which is placed anywhere in the ODScrollView.
     - IfNeeded : ODScrollView only adjusts the UITextInput if it overlaps with the shown keyboard.
     */
    func adjustmentOption(for scrollView: ODScrollView) -> AdjustmentOption {
        .Always
    }

    // MARK: - Hiding Keyboard Settings

    /**
     #Optional

     Provides a view for tap gesture that hides keyboard.

     By default, keyboard can be dismissed by keyboardDismissMode of UIScrollView.

     keyboardDismissMode = .none
     keyboardDismissMode = .onDrag
     keyboardDismissMode = .interactive

     Beside above settings:

     - Returning UIView from this, lets you to hide the keyboard by tapping the UIView you provide, and also be able to use isResettingAdjustmentEnabled(for scrollView: ODScrollView) setting.

     - If you return nil instead of UIView object, It means that hiding the keyboard by tapping is disabled.
     */
    func hideKeyboardByTappingToView(for scrollView: ODScrollView) -> UIView? {
        self.view
    }

    /**
     #Optional

     Resets the scroll view offset - which is adjusted before - to beginning its position after keyboard hid by tapping to the provided UIView via hideKeyboardByTappingToView.

     ## IMPORTANT:
     This feature requires a UIView that is provided by hideKeyboardByTappingToView().
     */
    func isResettingAdjustmentEnabled(for scrollView: ODScrollView) -> Bool {
        true
    }
}

7 -可选:在键入多行 UITextInput 时,当光标与键盘重叠时,可以调整 ODScrollView。trackTextInputCursor(for UITextInput) 必须由键入时触发的 UITextInput 函数调用。

/**
## IMPORTANT:
This feature is not going to work unless textView is subView of _ODScrollView
*/
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
       _ODScrollView.trackTextInputCursor(for textView)
   return true
}
0赞 2 revs, 2 users 69%keshav #96

您不需要仅仅为了一个简单的任务而需要这么多代码。有一个名为“IQKeyboardManager”的 CocoaPod 将为您完成任务:

pod 'IQKeyboardManager'

然后在从以下位置返回之前使用此代码:AppDelegatedidFinishLaunchingWithOptions

IQKeyboardManager.shared().isEnabled = true
IQKeyboardManager.shared().shouldResignOnTouchOutside = true
IQKeyboardManager.shared().isEnableAutoToolbar = false
1赞 2 revs, 2 users 71%steveSarsawa #97

Dune Buggy 的答案中得到了参考。根据 的位置向上滚动视图。由于现有答案是滚动屏幕的整个视图,因此我需要根据文本字段的框架滚动视图。TextField

  • KeyBoardWillShow
@objc func keyboardWillShow(notification: NSNotification) {
    
    guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
        
        // if keyboard size is not available for some reason, don't do anything
        return
    }
    let keyboardFrame = keyboardSize
    
    let maxheightScreen = self.view.frame
    
    
    if (self.txtEmail.frame.origin.y + ((self.txtEmail.superview)!.frame.maxY) + keyboardSize.height) >= maxheightScreen.size.height{
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= (keyboardFrame.height - (self.txtEmail.frame.maxY + 120)) // Here i added 120 additional height for my additional view space
        }
    }
}
  • 键盘将隐藏
@objc func keyboardWillHide(notification: NSNotification) {
    
    guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
        // if keyboard size is not available for some reason, don't do anything
        return
    }
    
    let keyboardFrame = keyboardSize
    
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y += (keyboardFrame.height - (self.txtEmail.frame.maxY + 120))
    }
}
0赞 2 revs, 2 users 91%goldena #98

这是我的“仅扩展”解决方案,建立在 Paul Hudson @twostraws 的解决方案之上(关于他和所有类似答案的作者)。UITextView

import UIKit

extension UITextView {
    
    func adjustableForKeyboard() {
        let notificationCenter = NotificationCenter.default
        
        notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
        notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    }
    
    @objc private func adjustForKeyboard(notification: Notification) {
        guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
            return
        }

        let keyboardScreenEndFrame = keyboardValue.cgRectValue
        let keyboardViewEndFrame = convert(keyboardScreenEndFrame, from: window)
        
        if notification.name == UIResponder.keyboardWillHideNotification {
            contentInset = .zero
        } else {
            contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardViewEndFrame.height - safeAreaInsets.bottom, right: 0)
        }

        scrollIndicatorInsets = contentInset
        scrollRangeToVisible(selectedRange)
    }
}

用法:

override func viewDidLoad() {
    super.viewDidLoad()
    
    textView.adjustableForKeyboard()
}
1赞 Ilya Biltuev #99

斯威夫特 5

我创建了一个简单的解决方案,100% 原生 Swift,没有任何第三方库。它将自动处理键盘位置。

只需将此类 KeyboardAppearListener 复制到您的项目中即可。

import UIKit

class KeyboardAppearListener {
    private weak var viewController: UIViewController?

    private var isKeyboardShown: Bool = false

    init(viewController: UIViewController) {
        self.viewController = viewController

        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardWillShow(notification:)),
            name: UIResponder.keyboardWillShowNotification,
            object: nil
        )
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardWillHide(notification:)),
            name: UIResponder.keyboardWillHideNotification,
            object: nil
        )
    }

    @objc private func keyboardWillShow(notification: Notification) {
        guard
            let viewController = viewController,
            let userInfo = notification.userInfo,
            let beginKeyboardFrame = userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect,
            let endKeyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
            endKeyboardFrame != beginKeyboardFrame,
            isKeyboardShown == false
        else {
            return
        }

        let windowSafeArea: CGFloat = viewController.view.safeAreaInsets.bottom - viewController.additionalSafeAreaInsets.bottom

        viewController.additionalSafeAreaInsets.bottom += (beginKeyboardFrame.origin.y - endKeyboardFrame.origin.y - windowSafeArea)

        animateUpdates(userInfo, viewController)
        isKeyboardShown = true
    }

    @objc private func keyboardWillHide(notification: Notification) {
        guard
            let viewController = viewController,
            let userInfo = notification.userInfo,
            let beginKeyboardFrame = userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect,
            let endKeyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
            isKeyboardShown == true
        else {
            return
        }

        let windowSafeArea: CGFloat = viewController.view.safeAreaInsets.bottom - viewController.additionalSafeAreaInsets.bottom

        viewController.additionalSafeAreaInsets.bottom -= (endKeyboardFrame.origin.y - beginKeyboardFrame.origin.y - windowSafeArea)

        animateUpdates(userInfo, viewController)
        isKeyboardShown = false
    }

    private func animateUpdates(_ userInfo: [AnyHashable: Any], _ viewController: UIViewController) {
        let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey]
            .flatMap { $0 as? Double } ?? 0.25

        if duration > 0 {
            let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey]
                .flatMap { $0 as? Int }
                .flatMap { UIView.AnimationCurve(rawValue: $0) } ?? .easeInOut

            UIViewPropertyAnimator(duration: duration, curve: curve) {
                viewController.view.layoutIfNeeded()
            }
            .startAnimation()
        } else {
            viewController.view.layoutIfNeeded()
        }
    }
}

用法:

class MyViewController: UIViewController {
    private var keyboardHandler: KeyboardAppearListener?

    override func viewDidLoad() {
        super.viewDidLoad()
        keyboardHandler = KeyboardAppearListener(viewController: self)
    }
}

评论

0赞 Fattie 12/16/2023
在实践中,你不知道你的设计师(或你)想要上下移动什么。这是久经考验的压倒性流行的解决方案:stackoverflow.com/a/41808338/294884