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


1691

使用iOS SDK:

我有一个UIView带有UITextFields的键盘。我需要它能够:

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

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

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

我需要a UIView和a UIScrollView吗?一个人会进入另一个人吗?

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

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

注意:我正在使用的UIView(或UIScrollView)由标签栏(UITabBar)调出,该标签栏需要正常运行。


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

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

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

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

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


6
看一下这个。不用为您烦恼。TPKeyboardAvoiding
Aruna

21
It's记录了苹果,我认为这是最好的办法:developer.apple.com/library/ios/#documentation/StringsTextFonts/...
Maik639

58
使用此代码。您只需在appdelegate.m文件中添加1行即可。github.com/hackiftekhar/IQKeyboardManager
Pradeep Mittal

9
到目前为止,我发现的最好方法是使用此开源TPKeyboardAvoiding
Zaidi

2
另一种方法是在TableViewController中添加此类内容文本字段以及所有内容,然后由tableview处理。
Vicky Dhas '17

Answers:


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

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

这是一些示例代码:

#define kOFFSET_FOR_KEYBOARD 80.0

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

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

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:mailTf])
    {
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:YES];
        }
    }
}

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

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

    [UIView commitAnimations];
}


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

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

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

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

3
_textField是什么?我将其复制到我的代码中,它说未声明_textField。
可可Dev

在该字段中,您通常会说“当用户在此处进行编辑时,视图应向上滑动”或类似的内容。但是,如果有更多字段,则可以将其删除。
帕特里克

在keyBoardWillSHow和KeyBoardWillHide事件中调用-(void)setViewMovedUp:(BOOL)movedUp是不是连击!
Abduliam Rehmanius 2011年

4
如果您要支持主视图的旋转,则它不是特别有用。
FractalDoctor

2
为了使这项工作,我不得不注释掉该textFieldDidBeginEditing部分。
AVANCE

445

我也遇到了很多问题,其中UIScrollView有多个UITextFields,其中一个或多个在编辑时会被键盘遮盖。

如果您的UIScrollView滚动不正确,请考虑以下几点。

1)确保您的contentSize大于UIScrollView框架大小。理解的方式UIScrollViews是,UIScrollView就像在contentSize中定义的内容上的查看窗口一样。因此,为了UIScrollview使滚动到任何位置时,contentSize必须大于UIScrollView。否则,不需要滚动,因为contentSize中定义的所有内容都已经可见。顺便说一句,默认contentSize = CGSizeZero

2)既然您了解UIScrollView的确是您“内容”的窗口,那么确保键盘不会遮挡您UIScrollView's查看的“窗口”的方法将是调整UIScrollView键盘的大小,以便在存在键盘时拥有该UIScrollView窗口大小仅为原始UIScrollViewframe.size.height减去键盘的高度。这将确保您的窗口仅是一个较小的可见区域。

3)这就是要注意的问题:第一次实现此功能时,我认为我必须获取CGRect已编辑文本字段的,并调用UIScrollView'sscrollRecToVisible方法。我通过调用UITextFieldDelegate方法实现了textFieldDidBeginEditingscrollRecToVisible方法。这实际上与一个奇怪的副作用是滚动将工作UITextField到位。在最长的时间内,我不知道那是什么。然后我注释掉了textFieldDidBeginEditingDelegate方法,所有方法都起作用了!(???)。事实证明,我相信UIScrollView实际上会隐式地将当前编辑的内容隐式地带UITextField入可见窗口。我对该UITextFieldDelegate方法的实现以及随后对的调用scrollRecToVisible是多余的,并且是造成奇怪副作用的原因。

因此,这里是正常滚动的步骤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. 确保contentSize已设置并大于您UIScrollViewviewDidLoad
  4. 收缩UIScrollView当键盘存在
  5. 退回UIScrollView键盘消失将其。
  6. 使用的ivar如果键盘已经显示在屏幕上,以检测由于键盘通知各一次发送UITextField被标签即使键盘已经存在,以避免收缩UIScrollView时候它已经缩水

需要注意的一件事是,UIKeyboardWillShowNotification即使您在另一键盘上按Tab键,屏幕上也已经有键盘时,它也会触发UITextField。我通过使用ivar来避免此问题,以避免UIScrollView在屏幕上已经有键盘的情况下调整大小。如果不经意调整了UIScrollView键盘的大小,那将是灾难性的!

希望这段代码可以使您省去很多麻烦。


3
很好,但是有两个问题:1. UIKeyboardBoundsUserInfoKey不推荐使用。2. keyboardSize在“屏幕坐标”中,因此如果框架旋转或缩放,则viewFrame计算将失败。
马丁·威克曼

21
@Martin Wickman-使用CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;而不是已弃用的UIKeyboardBoundsUserInfoKey
东西-sottenad

1
嗨,我做了同样的事情,但是文本视图仅在用户开始输入时才向上移动?这是预期的行为还是我错过了一些东西?

3
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size应该是[[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size。很好的解决方案!
j7nn7k 2012年

1
我喜欢您的解决方案,但我想我可以使它更简单:不要理会Notification Observer的东西;而是在适当的委托方法内调用正确的动画例程-对于UITextView,它们是textViewDidBeginEditing和textViewDidEndEditing。
AlexChaffee

270

实际上最好使用docs中提供的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];
}

Apple的代码存在以下问题:(1)它们始终计算该点是否在视图的框架内,但是它是一个ScrollView,因此它可能已经滚动了,您需要考虑该偏移量:

origin.y -= scrollView.contentOffset.y

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

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

1
在滚动视图未填满屏幕的情况下,应将aRect设置为滚动视图的框架
mblackwell8

2
您是否不应该希望CGPoint origin = activeField.frame.origin + activeField.frame.size.height?,因为您希望显示整个文本字段,并且如果它恰好具有可见的某些像素,则代码将不会输入健康)状况。
htafoya

1
此解决方案不适用于横向方向-文本字段从视口顶部飞出。装有iOS 7.1的iPad。
2014年

4
为了获得更好的iOS 8支持,我建议在获取键盘大小时使用UIKeyboardFrameEndUserInfoKey而不是UIKeyboardFrameBeginUserInfoKey,因为这会带来诸如自定义键盘更改和切换预测性文本开/关之类的事情。
Endareth

1
@Egor:您的修复使它工作得更好-但最后一行必须是相反的:self.scrollView.contentOffset = self.currentSVoffset;
Morten Holmgaard 2015年

244

textFieldDidBeginEdittingtextFieldDidEndEditing通话功能[self animateTextField:textField up:YES],如下所示:

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

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

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

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

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

希望这段代码对您有所帮助。

在Swift 2

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

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


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

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

SWIFT 3

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

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


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

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

1
为什么不使用 [UIView animateWithDuration: animations:^{ }];
Andre Cytryn

2
尽管const int movementDistance = -130,但效果很好。//需要根据需要进行调整以变得更加灵活
Hammer

7
在小型实现中非常简单。不会对ScrollViews和歧义自动布局问题感到困惑。
詹姆斯·佩里

4
嗯...您根本不使用textField参数。为什么然后将其作为函数参数?另外,您也可以在Swift中使用三元运算符。使代码少说话。
stk 2016年

1
如果“视图”的背景颜色不是黑色,请确保将“窗口”的颜色设置为与您的视图相匹配,以使用户看不到它的后面。即self.window.backgroundColor = [UIColor whiteColor];
bvmobileapps

134

仅使用TextFields:

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

1b)在称为scrollView的UIScrollView中手动嵌入TextField

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
但当它textField已经可见时,它也会向上移动视图。
TheTiger 2014年

1
需要更改CGPointMake(0, textField.frame.origin.y);CGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
Fury 2015年

@Egor即使在您发表评论后,它也不起作用。就像提到的“ TheTiger”一样,即使文本字段可见,它也会在视图中向上移动。
rak appdev

XCode 10的更改:“选择所有TextFields =>编辑器=>嵌入=>滚动视图”
tibalt

116

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

在此处输入图片说明

第一步: -我加了全球的通知UITextFieldUITextViewUIKeyboard在一个单独的类。我叫它 IQKeyboardManager

第二步: -如果找到UIKeyboardWillShowNotificationUITextFieldTextDidBeginEditingNotificationUITextViewTextDidBeginEditingNotification通知,我试图让topMostViewController从实例UIWindow.rootViewController层次结构。为了正确地揪出UITextField/ UITextView就可以了,topMostViewController.view需要调整的框架。

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

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

步骤5:-如果找到UIKeyboardWillHideNotificationUITextFieldTextDidEndEditingNotificationUITextViewTextDidEndEditingNotification发出通知,我再次尝试topMostViewControllerUIWindow.rootViewController层次结构。

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

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

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

这就是IQKeyboardManager真正为您完成的所有工作,而无需任何代码!只需要将相关的源文件拖放到项目中即可。IQKeyboardManager还支持“ 设备方向”,“ 自动UIToolbar管理”,“ KeybkeyboardDistanceFromTextField”等功能。


将IQKeyBoardManagerSwift目录添加到我的项目中,并且不起作用。无法启用cuz其无法在AppDelegate中识别...
user3722523 2015年

2
感觉好像没有显示网络钓鱼实际的解决方案,但是我们看到了这个家伙GitHub帐户的商业广告。
布赖恩

101

我已经把一个普遍的,插入式UIScrollViewUITableView甚至UICollectionView子类,拍摄运动中的所有文本字段出了键盘的方式照顾。

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

它基本上可以与任何设置一起使用,既可以是UITableView基于-的界面,也可以是由手动放置的视图组成的界面。

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


就是这个!这是最好,最有效,最完美的解决方案!它还可以正确处理旋转以进行滚动视图。如果旋转,请确保垂直自动调整大小,但不要锚定在底部。在我的情况下,我向滚动视图添加了UITextView。谢谢一束!
Christopher

非常好!当然,我懒于使用您的解决方案而不是自己动手做,但是我的老板更快乐,是的!即使有人确实想做自己,我也喜欢您的子类方法,而不是向每个控制器添加代码。我很震惊,iOS默认情况下没有像Android那样执行此操作-然后,再次,我发现iOS和MacOS缺少很多东西:(
eselk 2012年

像我的滚动视图之类的怪异事物是否都适合屏幕,因此无法滚动。打开和关闭键盘后,内容现在变大了(看起来像是在页面底部添加了不可见的内容,但未将其删除),并且可以滚动显示。
Almo 2014年

90

对于Swift程序员:

这将为您完成所有工作,只需将它们放在视图控制器类中,然后将实施UITextFieldDelegate到您的视图控制器,并将textField的委托设置为self

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的第一个响应者,但是随后您按下了一个按钮,该按钮会将您的Help View Controller推入堆栈。要解决偏移量错误,请确保在离开视图控制器之前调用nameField.resignFirstResponder(),并确保同时调用了textFieldDidEndEditing委托方法。我在viewWillDisappear方法中执行此操作。


3
SwiftLint不喜欢,self.view.frame = CGRectOffset(self.view.frame, 0, movement)所以我将该行更改为self.view.frame.offsetInPlace(dx: 0, dy: movement)
levibostian

2
Swift 4将self.view.frame = CGRectOffset(self.view.frame,0,运动)更改为self.view.frame.offsetBy(dx:0,dy:运动)
Asinox

仅供参考,要使其正常工作,您必须付出努力。self.view.frame = self.view.frame.offsetBy(dx:0,dy:运动)
Josh Wolff

64

已经有很多答案,但是上面的解决方案仍然没有一个具有“完美”的无错误,向后兼容和无闪烁动画所需的所有精美定位内容。(在同时对frame / bounds和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];
}

@Shiun答案的重大改进。但是在键盘消失后,视图不会回到第一位置。它仍然是一项伟大的工作:)
Lucien 2013年

2
谢谢,这是2017年对我来说最好的解决方案。请注意,您无需自己跟踪focusControl,您可以使用确定UIApplication.shared.sendAction(...)。这是您的答案的Swift 3版本(减去willHide部分),sendAction实现如下:gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72d
xaphod

在我的情况下,@ xaphod我需要关注更多控件-例如,输入字段下方的按钮。但是是的,该代码现在已有4年历史了,可能会从改进中受益。
Martin Ullrich

这可能是正确的解决方案。键盘通知包含动画数据,文本字段委托不知道动画持续时间,这只是猜测。
XY

62

Shiun表示:“事实证明,我相信UIScrollView实际上隐式地将当前编辑的UITextField隐式地带到可见窗口中”,这对于iOS 3.1.3似乎是正确的,但对于3.2、4.0或4.1则不成立。我必须添加一个显式的scrollRectToVisible,以使UITextField在iOS> = 3.2上可见。


不是UIScrollView隐式地将编辑的UITextField滚动到视图中,而是UITextField调用了私有[UITextField scrollTextFieldToVisibleIfNecessary]方法,该私有方法又在被调用[UIScrollView scrollRectToVisible][UITextField becomeFirstResponder]调用。见github.com/leopatras/ios_textfields_on_scrollview。如果约束和视图控制器设置正确,则实际上无需scrollRectToVisible显式调用(至少从IOS 11开始)。
狮子座

48

要考虑的一件事是您是否想单独使用a UITextField。我还没有碰到过任何精心设计的iPhone应用程序实际使用UITextFields之外UITableViewCells

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


1
我的一个应用程序需要允许用户添加自由格式的注释-是的,使用UITextField有时会很有用。
彼得·约翰逊

1
我同意这种方法。零工作或以这种方式编码。即使您需要免费的备忘,您仍然可以使用表格单元格
RJH 2015年

UITableView可悲的是唯一的方法。键盘通知比较脆弱,并且随着时间的推移已更改。堆栈溢出的示例代码:stackoverflow.com/a/32390936/218152
SwiftArchitect

这个答案已经过时五年了。唯一的现代解决方案是这样的... stackoverflow.com/a/41808338/294884
Fattie

47

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

编辑:注意在示例中有一个小故障。您可能想UIKeyboardWillHideNotification代替听UIKeyboardDidHideNotification。否则,在键盘关闭动画期间,将修剪键盘后面的滚动视图。


32

找到最简单的解决方案

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

即使屏幕不在底部,屏幕也会向上移动。即,如果文本字段位于顶部,它将移出屏幕。如何控制这种情况?
MELWIN,2014年

@MELWIN只需在此行之后添加:int movement = (up ? -movementDistance : movementDistance); if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 }请不要使keyboard变量是通过执行以下操作弹出的键盘的CGRect:let keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
CapturedTree

31

适用于许多UITextField的小修复

#pragma mark UIKeyboard handling

#define kMin 150

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



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

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

   [UIView commitAnimations];
}


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

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

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

}


- (void)viewWillAppear:(BOOL)animated
{
   // register for keyboard notifications
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) 
                                                name:UIKeyboardWillShowNotification object:self.view.window]; 
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) 
                                                name:UIKeyboardWillHideNotification object:self.view.window]; 
}

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

rect.origin.y=+currTextField.frame.origin.y工作很好,谢谢
u.gen 2012年

30

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

他的以下代码不应该使视图降低吗?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}

23

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


该文章的链接已失效。
张志贤

22

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

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

20

试试这个短招。

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

19

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

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

PS:我希望代码能帮助某人迅速取得预期的效果。(Xcode 4.5)


嗨,Hotjard,我在[self.view addSubview:natView]中得到EXE_BAD_ACCESS;
Bala 2014年

18

@ user271753

要将视图恢复为原始添加,请执行以下操作:

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

16

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

要使用此子类,请将您的自定义视图控制器作为GMKeyboardVC的子类,并且它继承了此功能(只要确定您是否实现了viewWillAppearviewWillDisappear它们必须调用super即可)。该类在github上


什么许可证?那里的某些文件具有开源许可证,而有些则没有。
杰米

警告:此代码不适用于ARC项目。
Almo 2014年

您只需添加构建选项即可指定这些文件是非ARC文件,或者欢迎将其转换为ARC并提交拉取请求。
progrmr

14

斯威夫特4

您可以轻松地上下移动,UITextField或者UIViewUIKeyBoardAnimation 在此处输入图片说明

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
几乎完美。在iPhone X上,尽管键盘和文本字段之间存在奇怪的差距。
侯曼

12

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

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

我有一个高度为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,而且我没有几天要学习内部布局引擎。


12

根据docs,从iOS 3.0开始,在UITableViewController对文本字段进行行内编辑时,该类会自动调整其表视图的大小并重新定位。我认为将文本字段放在UITableViewCell

文档

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


我发现了同样的评论。是的,它是真实的。奇怪的是,它在一个UITabelViewController中工作,而在另一个UITabelViewController中工作。但是我在实现上找不到任何差异。
Morpheus78

11

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

您只需要在示例代码下方复制粘贴,然后更改文本字段或要向上移动的任何视图。

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

第三步

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

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

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

希望这对外面的人有帮助。


我认为这不是最好的。Ithink @Dheeraj VS是正确的:如果该文本字段在表的单元格中(即使在table.scrollable = NO时),也可以轻松自动地完成。 注意:桌子的位置和大小必​​须合理。例如:-如果表格的y位置从视图的底部算起100,则高度为300的键盘将与整个表格重叠。-如果表格的高度= 10,并且在出现键盘时必须向上滚动其中的文本字段以使其可见,则该文本字段将超出表格的范围。
samthui7 2016年

@ samthui7 Dheeraj答案仅在您使用TableViewController时才有效,而不仅仅是tableview。这使它有时是不合适的约束。
本G

10

一直在寻找适合该主题的初学者的良好教程,在这里找到了最好的教程

MIScrollView.h教程底部的示例中,请确保在空格处

@property (nonatomic, retain) id backgroundTapDelegate;

正如你看到的。


savagenoob,您好,感谢提供的链接,欢迎您使用stackoverflow。回答(未来)问题时,请尝试提供尽可能多的信息-简单的链接有些脆弱。就是说,如果答案是一个好的教程的链接,那么它可能会被忽略。
Maarten Bodewes 2012年

10

什么时候UITextFieldUITableViewCell滚动应自动进行设置。

如果不是,则可能是由于错误的tableview代码/设置。

例如,当我重新装入长桌时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];
}

我很困惑这段代码是干什么用的?显示键盘时,viewWillAppear不会被调用。并且reloadData不会使模糊的行变得可见。
亚当·约翰斯


8

注意:此答案假定您的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)。

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

请注意,对于文本字段,我们在didBeginEditing中设置它,对于textView在shouldBeginEditing中进行设置。这是因为由于某种原因,在UIKeyboardWillShowNotification之后会调用textViewDidBeginEditing。

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

8

这是使用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
    }

}

正确答案,但同时使用TextField和TextView时我没有问题。有帮助吗?
蒂哈·昂

@Thiha Aung,源代码中的IBOutlet变量是否已连接到IB?
Homam'1

是的,它们也已连接。在该行使用UITextView时遇到该错误:if(!CGRectContainsPoint(aRect,self.activeTextField.frame.origin)){
Thiha Aung

表示self.activeTextField为零
Thiha Aung
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.