【问题标题】:How can I make a UITextField move up when the keyboard is present - on starting to edit?当键盘存在时,如何使 UITextField 向上移动 - 开始编辑?
【发布时间】:2010-11-10 17:38:30
【问题描述】:

使用 iOS SDK:

我有一个带有UITextFields 的UIView,可以调出键盘。我需要它能够:

  1. 一旦键盘被调出,允许滚动UIScrollView 的内容以查看其他文本字段

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

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

我需要UIViewUIScrollView 吗?一个会进入另一个吗?

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

理想情况下,尽可能多的组件设置将在 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
}

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

【问题讨论】:

标签: ios objective-c uiscrollview uitextfield uikeyboard


【解决方案1】:

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

override var inputAccessoryView: UIView? {
    return <yourTextField>
}

【讨论】:

    【解决方案2】:

    这是我的“仅 UITextView 扩展”解决方案,基于 Paul Hudson @twostraws 的解决方案(在此向他和所有类似答案的作者致敬)。

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

    【讨论】:

      【解决方案3】:

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

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

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

      但是这种方法需要在 UIView 需要切换的每个控制器上添加。 我一直在使用 'TPKeyboardAvoiding' pod 。无论您是 Scrollview 、 TableView 还是 CollectionView ,它都可以可靠且轻松地处理每种可能情况下的 UIView 转换。您只需要将课程传递给您的“滚动视图”。

      如下图

      如果您是 tableview ,则可以将此类更改为 'TPKeyboardAvoidingTableView'。 可以找到完整的运行项目Project Link

      我一直在遵循这种稳健的开发方法。希望这会有所帮助!

      【讨论】:

        【解决方案4】:

        SwiftUI 使用 ViewModifier

        你可以使用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() 会自动在你的视图中添加一个 padding,这样更优雅。

        【讨论】:

          【解决方案5】:

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

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

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

          // // 键盘感知控制器.h // 社会病态 // // 由管理员于 13.01.14 创建。 // 版权所有 (c) 2014 kuchumovn。版权所有。 //

          #import <UIKit/UIKit.h>
          
          @interface KeyboardAwareController : UIViewController <UITextFieldDelegate>
          
          @end
          

          // // KeyboardAwareController.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

          为了更好的解释,我还截图了:

          【讨论】:

          • 从对此答案投反对票的人那里获得一些反馈会很有用。
          【解决方案6】:

          参考以下

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

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

            • 最好在代码中包含一些上下文/解释,因为这会使答案对 OP 和未来的读者更有用。
            【解决方案8】:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-11-30
              • 1970-01-01
              • 2015-10-17
              • 1970-01-01
              • 2010-09-21
              相关资源
              最近更新 更多