【问题标题】:How to resize UITextView on iOS when a keyboard appears?出现键盘时如何在iOS上调整UITextView的大小?
【发布时间】:2011-11-02 10:31:28
【问题描述】:

UITextView 插入到UITabBarController 的标签中(在 iPhone 上)。

  1. 用很多行填充UITextView
  2. 显示键盘以编辑文本。

发生了什么?键盘用光标隐藏了UITextView 的一半。结果无法编辑文本。

如何解决所有 Apple 移动设备(屏幕分辨率不同)的问题?非常感谢您的帮助!

【问题讨论】:

    标签: iphone keyboard uitextview


    【解决方案1】:

    以下代码达到了最好的结果。另外不要忘记将背景颜色设置为 UIView 并将 UITextView 放在其他顶部屏幕控件(例如 UITabBar)之前。

    文本的最终编辑现在仍然不完美。你可以尝试改进。

    FirstViewController.h:

    @interface FirstViewController : UIViewController {
        IBOutlet UIBarButtonItem *buttonDone;
        IBOutlet UITextView *textView;
        UITabBarController* tabBarController; // set from superview in AppDelegate (MainWindow.xib)
    }
    
    @property (nonatomic, retain) UITabBarController* tabBarController;
    

    FirstViewController.m:

    @synthesize tabBarController;
    
    - (void)viewDidAppear:(BOOL)animated
    {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
    }
    
    - (void)viewWillDisappear:(BOOL)animated
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    - (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up {
        NSDictionary* userInfo = [aNotification userInfo];
        NSTimeInterval animationDuration;
        UIViewAnimationCurve animationCurve;
        CGRect keyboardEndFrame;
    
        [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
        [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
        [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
    
        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:animationDuration];
        [UIView setAnimationCurve:animationCurve];
    
        CGRect newFrame = textView.frame;
        CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
        keyboardFrame.size.height -= tabBarController.tabBar.frame.size.height;
        newFrame.size.height -= keyboardFrame.size.height * (up?1:-1);
        textView.frame = newFrame;
    
        [UIView commitAnimations];   
    }
    
    - (void)keyboardWillShown:(NSNotification*)aNotification
    {
        buttonDone.enabled = true;
        [self moveTextViewForKeyboard:aNotification up:YES]; 
    }
    
    - (void)keyboardWillHide:(NSNotification*)aNotification
    {
        buttonDone.enabled = false;
        [self moveTextViewForKeyboard:aNotification up:NO]; 
    }
    

    附:没有 stackoverflow 很难为 iOS 编写代码......

    【讨论】:

    • 我知道这有点不相关且陈旧,但我们如何在自定义 UITableViewCell 中使用 UITextField 做类似的事情?
    • 我无法让此解决方案在 iOS 7 中运行 - 请参阅以下内容:stackoverflow.com/questions/18968735/…
    • 答案不再真实。看看新的。
    【解决方案2】:

    使用自动布局,处理起来要容易得多(前提是您了解自动布局):

    您无需尝试识别受影响的视图并调整其大小,只需为您的所有视图内容创建一个父框架。然后,如果 kbd 出现,您调整框架的大小,并且如果您正确设置了约束,视图将很好地重新排列其所有子视图。无需为此摆弄大量难以阅读的代码。

    事实上,在similar question 中,我找到了指向此excellent tutorial 的有关此技术的链接。

    【讨论】:

      【解决方案3】:

      我在尝试让我的文本视图在 iOS 7 和 iOS 8 以及新的 QuickType 功能下正确滚动和动画时遇到了几个问题。起初我专注于动画滚动视图插图,但 iOS 7 和 8 之间的行为不同,无法让它在两者上正常工作。

      然后我意识到我可以通过只关注框架来简化事情,这对我来说可以使用更简单的代码。总结:

      • 注册UIKeyboardDidChangeFrameNotification(这将在显示/隐藏 QuickType 时通知)。
      • 算出你需要多少垂直空间来改变你的文本视图的框架。
      • 动画帧大小变化。

      以下是一些说明上述内容的代码:

      - (void)viewDidLoad {
          [super viewDidLoad];
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrameWithNotification:) name:UIKeyboardDidChangeFrameNotification object:nil];
      }
      
      - (void)keyboardDidChangeFrameWithNotification:(NSNotification *)notification {
          CGFloat keyboardVerticalIncrease = [self keyboardVerticalIncreaseForNotification:notification];
          [self animateTextViewFrameForVerticalOffset:keyboardVerticalIncrease];
      }
      
      - (CGFloat)keyboardVerticalIncreaseForNotification:(NSNotification *)notification {
          CGFloat keyboardBeginY = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].origin.y;
          CGFloat keyboardEndY = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y;
          CGFloat keyboardVerticalIncrease = keyboardBeginY - keyboardEndY;
          return keyboardVerticalIncrease;
      }
      
      - (void)animateTextViewFrameForVerticalOffset:(CGFloat)offset {
          CGFloat constant = self.bottomConstraint.constant;
          CGFloat newConstant = constant + offset;
          self.bottomConstraint.constant = newConstant;
          [self.view layoutIfNeeded];
          [UIView animateWithDuration:0.5 animations:^{
              [self.view layoutIfNeeded];
          }];
      }
      

      关于动画的简要说明。我使用了 Autolayout,所以我选择为文本视图的 NSAutoLayoutConstraint 设置动画,而不是直接为框架设置动画。为此,我在动画块内的 之前调用 [self.view layoutIfNeeded]。这是动画约束的正确方法。我找到了这个提示here

      【讨论】:

      • 很简单的方法,我喜欢!
      【解决方案4】:

      值得注意的是,仅当设备处于纵向模式(而不是倒置)时,赞成的答案才有效,在其他模式下,边界会出错。我相信你可以通过使用边界来修复这个问题,但我无法让它工作,所以下面的调整对我有用:

      - (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up {
      
      
      NSDictionary* userInfo = [aNotification userInfo];
      NSTimeInterval animationDuration;
      UIViewAnimationCurve animationCurve;
      CGRect keyboardEndFrame;
      
      [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
      [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
      [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
      
      
      [UIView beginAnimations:nil context:nil];
      [UIView setAnimationDuration:animationDuration];
      [UIView setAnimationCurve:animationCurve];
      
      CGRect newFrame = self.view.frame;
      
      if (keyboardEndFrame.size.height >keyboardEndFrame.size.width)
      {   //we must be in landscape
          if (keyboardEndFrame.origin.x==0)
          {   //upside down so need to flip origin
              newFrame.origin = CGPointMake(keyboardEndFrame.size.width, 0);
          }
      
          newFrame.size.width -= keyboardEndFrame.size.width * (up?1:-1);
      
      } else
      {   //in portrait
          if (keyboardEndFrame.origin.y==0)
          {
              //upside down so need to flip origin
              newFrame.origin = CGPointMake(0, keyboardEndFrame.size.height);
          }
          newFrame.size.height -= keyboardEndFrame.size.height * (up?1:-1);
      
      }
      self.view.frame = newFrame;
      
      [UIView commitAnimations];
      
      
      
      }
      

      【讨论】:

        【解决方案5】:

        多年过去了,问题仍然存在。苹果绝对应该自己处理所有这些事情。但事实并非如此。这是基于官方 Apple 的 documentation 以及错误修复的新解决方案。它支持 iOS 8、iOS 9、inputAccessoryView 并为新版本的 iOS 和新设备做好准备。

        /* Apple's solution to resize keyboard but with accessory view support */
        
        - (void)keyboardDidShow:(NSNotification*)aNotification {
            NSDictionary* info = [aNotification userInfo];
            CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
            double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.origin.y;
        
            UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
            editor.contentInset = contentInsets;
            editor.scrollIndicatorInsets = contentInsets;
        }
        
        - (void)keyboardWillHide:(NSNotification*)aNotification {
            UIEdgeInsets contentInsets = UIEdgeInsetsZero;
            editor.contentInset = contentInsets;
            editor.scrollIndicatorInsets = contentInsets;
        
            // button to hide the keyboard
            buttonDone.enabled = false;
        }
        
        /* Fix issues with size classes and accessory view */
        
        - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
            [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
            // fix incorrect size of the inputAccessoryView when size class changed
            // willTransitionToTraitCollection and traitCollectionDidChange can't help us
            if (editor && editor.inputAccessoryView && !editor.inputAccessoryView.hidden) {
                [editor resignFirstResponder];
            }
        }
        
        /* Hide accessory view if a hardware keyboard is present */
        
        #define gThresholdForHardwareKeyboardToolbar 160.f // it's minimum height of the software keyboard on iPhone 4 in landscape mode
        
        - (bool)isExternalKeyboard:(NSNotification*)aNotification {
            NSDictionary* info = [aNotification userInfo];
            CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
            double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.origin.y;
        
            return keyboardHeight < gThresholdForHardwareKeyboardToolbar;
        }
        
        - (void)keyboardWillShow:(NSNotification*)aNotification {
            if ([self isExternalKeyboard:aNotification]) {
                // hardware keyboard is present
                if (editor && editor.inputAccessoryView) {
                    editor.inputAccessoryView.hidden = true;
                }
            } else {
                // only on-screen keyboard
                if (editor && editor.inputAccessoryView) {
                    editor.inputAccessoryView.hidden = false;
                }
            }
        
            // button to hide the keyboard
            buttonDone.enabled = true;
        }
        

        【讨论】:

        • 这也是 iOS 10 的答案。注意其他答案的年份。我确信他们在旧操作系统上工作,但这个在 iOS 10 上对我来说完美。
        【解决方案6】:
        - (void)registerKeyboardNotifications
        {
            [[NSNotificationCenter defaultCenter]
             addObserver:self
             selector:@selector(keyboardWillShow:)
             name:UIKeyboardDidShowNotification
             object:nil];
        
            [[NSNotificationCenter defaultCenter]
             addObserver:self
             selector:@selector(keyboardWillHide:)
             name:UIKeyboardWillHideNotification
             object:nil];
        }
        
        - (void)unregisterKeyboardNotifications
        {
            [[NSNotificationCenter defaultCenter] removeObserver:self];
        }
        
        -(void) keyboardWillHide:(NSNotification *)note
        {
           //adjust frame
        }
        
        -(void) keyboardWillShow:(NSNotification *)note
        {
           //adjust frame 
        }
        

        在dealloc中也取消注册通知

        - (void)unregisterKeyboardNotifications
        {
            [[NSNotificationCenter defaultCenter] removeObserver:self];
        }
        

        【讨论】:

          【解决方案7】:

          首先在NSNotificationCenterdefaultCenter 中添加几个键盘方法

          [[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)keyboardWillShow:(NSNotification *)notif
          {
          [thetextView setFrame:CGRectMake(20, 49, 280, 187)]; //Or where ever you want the view to go
          
          
          }
          
          - (void)keyboardWillHide:(NSNotification *)notif
          {
          [thetextView setFrame:CGRectMake(20, 49, 280, 324)]; //return it to its original position
          
          }
          

          【讨论】:

          • 但是 iPad、iPhone 4 和未来的新设备呢?此代码不适用于所有这些!
          • 作为后续,我查看了 Apple 的文档。 UIKeyboardWillShow/Hide 不被弃用。我展示的代码适用于所有设备,您可能只需要修改不同设备的坐标(只需一个简单的 UIDevice if 语句)
          • 尝试启用日文键盘 - 他们有一个可以使键盘更高的预输入。从键盘上拉出尺寸并制作动画曲线确实是正确的方法。
          • 硬编码坐标永远不是可行的方法。
          【解决方案8】:

          简而言之,注册键盘通知并在收到通知时进行调整大小的工作。

          【讨论】:

          • 但是如何在 iPhone/iPad 上调整 UITextView 的大小以正确大小?
          【解决方案9】:

          作为后续,当键盘通知发生时更新框架的技术不适用于 iOS 7。有关替代解决方案,请参阅以下内容:

          How to re-size UITextView when keyboard shown with iOS 7

          【讨论】:

            【解决方案10】:

            我在这里尝试了最佳答案,但我发现其中有问题。如果您在同一页面上有另一个文本字段,请单击该文本字段,显示键盘。您会注意到文本视图缩小了。但是,如果您现在单击文本视图,您会注意到文本视图大小再次缩小,而它不应该缩小。

            我对这个问题的解决方案是在视图控制器中维护一个表示键盘状态(显示/隐藏)的属性。如果键盘当前可见,则不应缩小文本视图。如果您为不同的文本输入使用不同尺寸的键盘,您还应该保持旧的键盘尺寸。

            请注意,此解决方案也没有考虑不同的方向,这可能会影响您计算文本视图大小的方式。

            @implementation MyViewController {
                BOOL keyboardShown;
                NSInteger keyboardHeight;
            }
            
            - (void)moveTextViewForKeyboard:(NSNotification*)aNotification up: (BOOL) up{
                NSDictionary* userInfo = [aNotification userInfo];
                NSTimeInterval animationDuration;
                UIViewAnimationCurve animationCurve;
                CGRect keyboardEndFrame;
            
                [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
                [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
                [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
            
                [UIView beginAnimations:nil context:nil];
                [UIView setAnimationDuration:animationDuration];
                [UIView setAnimationCurve:animationCurve];
            
                CGRect newFrame = self.textView.frame;
                CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
            
                NSInteger oldHeight = self->keyboardShown ? self->keyboardHeight : 0;
                NSInteger newHeight = up ? keyboardFrame.size.height : 0;
                NSInteger change = oldHeight - newHeight;
            
                self->keyboardShown = up;
                self->keyboardHeight = keyboardFrame.size.height;
            
                newFrame.size.height += change;
                self.textView.frame = newFrame;
            
                [UIView commitAnimations];
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-04-09
              • 1970-01-01
              • 2013-09-28
              • 2017-05-03
              • 1970-01-01
              相关资源
              最近更新 更多