Xcode 当键盘向上移动时,如何使文本字段保持原位?敏捷的

Xcode 当键盘向上移动时,如何使文本字段保持原位?敏捷的,xcode,uistoryboard,Xcode,Uistoryboard,我创建了一个包含4个字段和一个按钮的表单。视图层次结构如下所示:主UIVIew、视图(重命名为contentView),在contentView之上,我有4个字段和1个编程创建的按钮 触发viewDidLoad时,按钮不会向上滚动,以便在contentView中可见 开始键入文本字段时,文本字段会在可视区域之外向上滚动 当firstResponder辞职(键盘隐藏)时,我无法滚动contentView。 我将按照上面指定的顺序列出图片截图 在此尝试之前,我将按钮固定在ViewController

我创建了一个包含4个字段和一个按钮的表单。视图层次结构如下所示:主UIVIew、视图(重命名为contentView),在contentView之上,我有4个字段和1个编程创建的按钮

  • 触发viewDidLoad时,按钮不会向上滚动,以便在contentView中可见
  • 开始键入文本字段时,文本字段会在可视区域之外向上滚动
  • 当firstResponder辞职(键盘隐藏)时,我无法滚动contentView。 我将按照上面指定的顺序列出图片截图
  • 在此尝试之前,我将按钮固定在ViewController的视图上,将按钮的底部约束指定给一个变量,当keyboardDidShow时,我将键盘大小添加到底部约束,从而将按钮拍摄到键盘上方。然而,stackoverflower表示,这种方法容易出现错误:

    我已经学习了本教程,但没有得到相同的结果
    鉴于Iphone有不同的屏幕尺寸,请建议最佳方法

    class EleventhViewController: UIViewController, UITextFieldDelegate {
    
     @IBOutlet weak var fullName: UITextField!
     @IBOutlet weak var flatNumber: UITextField!
     @IBOutlet weak var streetAddress: UITextField!
     @IBOutlet weak var phoneNumber: UITextField!
     @IBOutlet weak var contentView: UIView!
     @IBOutlet weak var scrollView: UIScrollView!
     var nextButtonOutlet:UIButton!
    
    override func viewDidLoad() {
          super.viewDidLoad()
        //called whenever keyboard is shown/hidden
     registerForKeyboardNotifications()   
    
         //when identifies single or multiple taps, call DismissKeyboard
     var tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "DismissKeyboard")
        contentView.addGestureRecognizer(tap)
    
    //create button programmatically
        var button = UIButton(type: UIButtonType.custom) as UIButton
          button = UIButton(frame: CGRect(x: 0, y: 637, width: 375, height: 50))
          button.titleLabel?.textColor = UIColor.white
          button.backgroundColor = UIColor(colorLiteralRed: 117/255, green: 232/255, blue: 0, alpha: 1)
          button.setTitle("Next", for: .normal)
          button.addTarget(self, action: #selector(EleventhViewController.nextButton), for: .touchUpInside)
           self.contentView.addSubview(button)
             self.nextButtonOutlet = button
    
    //disable scroll bouncing
       scrollView.bounces = false
    
        self.fullName.delegate = self
         self.flatNumber.delegate = self
          self.streetAddress.delegate = self
             self.phoneNumber.delegate = self
     }
    
    
       //Call this function when the tap is recognized.
         func DismissKeyboard(){
           contentView.endEditing(true)
      }
    
    
    
         // Stop Editing on Return Key Tap. 
         func textFieldShouldReturn(_ textField: UITextField) -> Bool {
           textField.resignFirstResponder()
            return true
      }
    
    
     weak var activeField: UITextField?
     func keyboardDidShow(_ notification: Notification) {
    
           //when a textfield is edited lift the button above the keyboard
          if let activeField = self.activeField,let keyboardSize =      
           (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as?  
                NSValue)?.cgRectValue {
            let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 
                 keyboardSize.height, right: 0.0)
                   self.scrollView.contentInset = contentInsets
    
      var aRect = self.view.frame
         aRect.size.height -= keyboardSize.size.height
    
          if !aRect.contains(nextButtonOutlet.frame.origin) {
              self.scrollView.scrollRectToVisible(nextButtonOutlet.frame, animated: true)
        }
     }
    
    
      func keyboardWillHide(_ notification: Notification) {
    
         let contentInsets = UIEdgeInsets.zero
          self.scrollView.contentInset = contentInsets
              self.scrollView.scrollIndicatorInsets = contentInsets
    }
    
        //Keep track of which textfield is being edited to make sure the field is visible when keyboard pops up
     func textFieldDidBeginEditing(_ textField: UITextField) {
            self.activeField = textField
     }
    
      func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {  
        self.activeField = nil
    }
    
        //register for keyboard notifications
          func registerForKeyboardNotifications() {
    
           NotificationCenter.default.addObserver(self, selector: 
          #selector(keyboardDidShow), 
            name: NSNotification.Name.UIKeyboardDidShow, object: nil)
    
           NotificationCenter.default.addObserver(self, selector:    
            #selector(keyboardWillHide), name: 
             NSNotification.Name.UIKeyboardWillHide, object: nil)
      }
    
     //remove keyBoard observers
      func deregisterFromKeyboardNotifications() {
        NotificationCenter.default.removeObserver(self, name: 
               NSNotification.Name.UIKeyboardDidShow, object: nil)
    
        NotificationCenter.default.removeObserver(self, name: 
             NSNotification.Name.UIKeyboardWillHide, object: nil)
        }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(true)
        //deregister keyboard notifications
             deregisterFromKeyboardNotifications()
         }
     } //end of class
    
       view Hierachy
    
    触发viewDidLoad时,按钮不显示

    开始键入文本字段时


    我认为只有当字段被键盘覆盖时,才应该向上移动字段,就像我几天前做的那样:

    let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue ?? NSValue()).cgRectValue
    let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
    UIView.animate(withDuration: 0.2) {
        if notification.name == Notification.Name.UIKeyboardWillHide {
            self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
        } else {
            let offset = (self.view.frame.size.height - self.activeField.frame.maxY) - keyboardViewEndFrame.height
            if offset < 0 {
                self.view.frame = CGRect(x: 0, y:  offset, width: self.view.width, height: self.view.height)
            } else {
                self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
            }
        }
    }
    
    将keyboardScreenEndFrame=(userInfo[UIKeyboardFrameEndUserInfoKey]设为?NSValue??NSValue()).cgRectValue
    让keyboardViewEndFrame=view.convert(keyboardScreenEndFrame,from:view.window)
    UIView.animate(持续时间:0.2){
    如果notification.name==notification.name.ui键盘将隐藏{
    self.view.frame=CGRect(x:0,y:0,width:self.view.width,height:self.view.height)
    }否则{
    让偏移量=(self.view.frame.size.height-self.activeField.frame.maxY)-keyboardViewEndFrame.height
    如果偏移量<0{
    self.view.frame=CGRect(x:0,y:offset,width:self.view.width,height:self.view.height)
    }否则{
    self.view.frame=CGRect(x:0,y:0,width:self.view.width,height:self.view.height)
    }
    }
    }
    
    基本上,您只需要为键盘处理计时添加逻辑,若键盘帧超过文本字段帧,您应该处理它。
    希望能有所帮助。

    我认为只有当字段被键盘覆盖时,才应该将其向上移动,就像我几天前做的那样:

    let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue ?? NSValue()).cgRectValue
    let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
    UIView.animate(withDuration: 0.2) {
        if notification.name == Notification.Name.UIKeyboardWillHide {
            self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
        } else {
            let offset = (self.view.frame.size.height - self.activeField.frame.maxY) - keyboardViewEndFrame.height
            if offset < 0 {
                self.view.frame = CGRect(x: 0, y:  offset, width: self.view.width, height: self.view.height)
            } else {
                self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
            }
        }
    }
    
    将keyboardScreenEndFrame=(userInfo[UIKeyboardFrameEndUserInfoKey]设为?NSValue??NSValue()).cgRectValue
    让keyboardViewEndFrame=view.convert(keyboardScreenEndFrame,from:view.window)
    UIView.animate(持续时间:0.2){
    如果notification.name==notification.name.ui键盘将隐藏{
    self.view.frame=CGRect(x:0,y:0,width:self.view.width,height:self.view.height)
    }否则{
    让偏移量=(self.view.frame.size.height-self.activeField.frame.maxY)-keyboardViewEndFrame.height
    如果偏移量<0{
    self.view.frame=CGRect(x:0,y:offset,width:self.view.width,height:self.view.height)
    }否则{
    self.view.frame=CGRect(x:0,y:0,width:self.view.width,height:self.view.height)
    }
    }
    }
    
    基本上,您只需要为键盘处理计时添加逻辑,若键盘帧超过文本字段帧,您应该处理它。
    希望能有所帮助。

    所以我想你已经接近了,我不确定这是代码问题还是自动布局问题。我猜你正在收到关于你的scrollview的投诉,因为你不知道内容大小,所以我将介绍这两个方面

    编辑: 另外,您的按钮必须位于我首先添加的视图容器下方,并且需要与scrollview分开处理。不要将其放在滚动视图中

    下面的方法适用于保存scrollview的视图,除了在布局底部添加50或其他方法。下面的方法也有助于进行编辑

    自动布局: 首先,对于只在页面上显示的表单,我喜欢先在故事板上添加一个视图,然后在顶部布局指南中固定(按钮所需的任何空间),左右。然后,我将我的ScrollView(将ScrollView固定到该视图)添加到刚才添加的视图中。然后,我将我的内容视图添加到ScrollView中。现在我将其固定到scrollview。您将看到autolayout仍然不开心。那么为什么是第一个视图,以及如何解决这个问题。我从contentView拖动到包含scrollview的视图,并选择等高等宽。现在,你将不会有自动布局尖叫你。注意:这适用于要填充第一个视图大小的内容,但允许其滚动以避免键盘。查看图像

    在添加这个相等的高度后,我可以继续使用故事板。我设置了文本字段。底部文本字段您可能希望也可能不希望将其固定在底部,但如果您确实使其>=您的号码

    编辑: 现在将“下一步”按钮添加到包含所有内容的视图下方的故事板。该按钮必须固定在主视图的底部,并带有0以粘贴到键盘上。现在看起来是这样的

    显然,这与初始图像有点冲突,但您所要做的只是增加底部布局指南的空间,只需确保您的按钮添加到主视图,而不是包含滚动视图的视图。现在将按钮连接到iboutlet中的控制器。我们需要它

    接下来,确保模拟器中有正确的键盘**不使用硬件键盘

    最后是代码。当我在子视图中循环设置委托时,您需要替换textfield变量。我还为向上滚动添加了填充。您应该将注销者移动到deint()。查看我的代码,最后您可能希望滚动键盘上的滚动视图将显示,而不是确实显示,但我没有改变这一点

    import UIKit
    
    class ViewController: UIViewController,UITextFieldDelegate {
    
    //added in storyboard. removed the code
    @IBOutlet weak var nextButton: UIButton!
    
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var contentView: UIView!
        override func viewDidLoad() {
            super.viewDidLoad()
            //called whenever keyboard is shown/hidden
    
            registerForKeyboardNotifications()
    
            //when identifies single or multiple taps, call DismissKeyboard
            var tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "DismissKeyboard")
            contentView.addGestureRecognizer(tap)
    
            //disable scroll bouncing
            scrollView.bounces = false
    
            //replace with your textfields
            for subs in self.contentView.subviews{
                if subs is UITextField{
                    print("setting")
                    (subs as! UITextField).delegate = self
                }
            }
        }
    
    
        //Call this function when the tap is recognized.
        func DismissKeyboard(){
            contentView.endEditing(true)
        }
    
    
    
        // Stop Editing on Return Key Tap.
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            textField.resignFirstResponder()
            return true
        }
    
        //edited for next button
        weak var activeField: UITextField?
        func keyboardDidShow(_ notification: Notification) {
    
            //when a textfield is edited lift the button above the keyboard
            if let activeField = self.activeField,let keyboardSize =
                (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as?
                    NSValue)?.cgRectValue {
    
                //20 in insets and offset is just padding
                let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom:
                    keyboardSize.height + 20 + nextButton.bounds.height, right: 0.0)
                self.scrollView.contentInset = contentInsets
    
                var aRect = self.view.frame
                aRect.size.height -= keyboardSize.height
    
    
                let bottomPoint = CGPoint(x: activeField.frame.origin.x, y:activeField.frame.origin.y)
    
                if aRect.contains(bottomPoint){
                    let scrollPoint = CGPoint(x: 0.0, y: bottomPoint.y - keyboardSize.height - 20 - nextButton.bounds.height)
                    scrollView.setContentOffset(scrollPoint, animated: true)
                }
    
            }
    
        }
            func keyboardWillHide(_ notification: Notification) {
    
                let contentInsets = UIEdgeInsets.zero
                self.scrollView.contentInset = contentInsets
                self.scrollView.scrollIndicatorInsets = contentInsets
            }
    
            //Keep track of which textfield is being edited to make sure the field is visible when keyboard pops up
            func textFieldDidBeginEditing(_ textField: UITextField) {
                self.activeField = textField
            }
    
    
            func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
                self.activeField = nil
            }
    
            //register for keyboard notifications
            func registerForKeyboardNotifications() {
    
                NotificationCenter.default.addObserver(self, selector:
                    #selector(keyboardDidShow),
                                                       name: NSNotification.Name.UIKeyboardDidShow, object: nil)
    
                NotificationCenter.default.addObserver(self, selector:
                    #selector(keyboardWillHide), name:
                    NSNotification.Name.UIKeyboardWillHide, object: nil)
            }
    
            //remove keyBoard observers
            func deregisterFromKeyboardNotifications() {
                NotificationCenter.default.removeObserver(self, name: 
                    NSNotification.Name.UIKeyboardDidShow, object: nil)
    
                NotificationCenter.default.removeObserver(self, name: 
                    NSNotification.Name.UIKeyboardWillHide, object: nil)
            }
    
    
    
            deinit {
                //deregister keyboard notifications
                deregisterFromKeyboardNotifications()
            }
    } //end of class
    
    现在一个
    import UIKit
    
    class ViewController: UIViewController,UITextFieldDelegate {
    
    //added in storyboard. removed the code
    @IBOutlet weak var nextButton: UIButton!
    
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var contentView: UIView!
        override func viewDidLoad() {
            super.viewDidLoad()
            //called whenever keyboard is shown/hidden
    
            registerForKeyboardNotifications()
    
            //when identifies single or multiple taps, call DismissKeyboard
            var tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "DismissKeyboard")
            contentView.addGestureRecognizer(tap)
    
            //disable scroll bouncing
            scrollView.bounces = false
    
            //replace with your textfields
            for subs in self.contentView.subviews{
                if subs is UITextField{
                    print("setting")
                    (subs as! UITextField).delegate = self
                }
            }
        }
    
    
        //Call this function when the tap is recognized.
        func DismissKeyboard(){
            contentView.endEditing(true)
        }
    
    
    
        // Stop Editing on Return Key Tap.
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            textField.resignFirstResponder()
            return true
        }
    
        //edited for next button
        weak var activeField: UITextField?
        func keyboardDidShow(_ notification: Notification) {
    
            //when a textfield is edited lift the button above the keyboard
            if let activeField = self.activeField,let keyboardSize =
                (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as?
                    NSValue)?.cgRectValue {
    
                //20 in insets and offset is just padding
                let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom:
                    keyboardSize.height + 20 + nextButton.bounds.height, right: 0.0)
                self.scrollView.contentInset = contentInsets
    
                var aRect = self.view.frame
                aRect.size.height -= keyboardSize.height
    
    
                let bottomPoint = CGPoint(x: activeField.frame.origin.x, y:activeField.frame.origin.y)
    
                if aRect.contains(bottomPoint){
                    let scrollPoint = CGPoint(x: 0.0, y: bottomPoint.y - keyboardSize.height - 20 - nextButton.bounds.height)
                    scrollView.setContentOffset(scrollPoint, animated: true)
                }
    
            }
    
        }
            func keyboardWillHide(_ notification: Notification) {
    
                let contentInsets = UIEdgeInsets.zero
                self.scrollView.contentInset = contentInsets
                self.scrollView.scrollIndicatorInsets = contentInsets
            }
    
            //Keep track of which textfield is being edited to make sure the field is visible when keyboard pops up
            func textFieldDidBeginEditing(_ textField: UITextField) {
                self.activeField = textField
            }
    
    
            func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
                self.activeField = nil
            }
    
            //register for keyboard notifications
            func registerForKeyboardNotifications() {
    
                NotificationCenter.default.addObserver(self, selector:
                    #selector(keyboardDidShow),
                                                       name: NSNotification.Name.UIKeyboardDidShow, object: nil)
    
                NotificationCenter.default.addObserver(self, selector:
                    #selector(keyboardWillHide), name:
                    NSNotification.Name.UIKeyboardWillHide, object: nil)
            }
    
            //remove keyBoard observers
            func deregisterFromKeyboardNotifications() {
                NotificationCenter.default.removeObserver(self, name: 
                    NSNotification.Name.UIKeyboardDidShow, object: nil)
    
                NotificationCenter.default.removeObserver(self, name: 
                    NSNotification.Name.UIKeyboardWillHide, object: nil)
            }
    
    
    
            deinit {
                //deregister keyboard notifications
                deregisterFromKeyboardNotifications()
            }
    } //end of class
    
    import UIKit
    
    class AvoidingConstraint: NSLayoutConstraint {
    
        private var offset : CGFloat = 0
        private var keyboardVisibleHeight : CGFloat = 0
    
        override public func awakeFromNib() {
            super.awakeFromNib()
    
            offset = constant
    
            NotificationCenter.default.addObserver(self, selector: #selector(AvoidingConstraint.keyboardWillShowNotification(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(AvoidingConstraint.keyboardWillHideNotification(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        }
    
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
    
        // MARK: Notification
    
        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
                }
    
            }
    
        }
    
        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
        }
    
    }