键盘向上移动时如何使文本字段保持原位?迅速 [英] How to make textFields stay in place when keyboard moves up? Swift

查看:38
本文介绍了键盘向上移动时如何使文本字段保持原位?迅速的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  1. 当 viewDidLoad 被触发时,按钮不会向上滚动,因此它可以在 contentView 中可见.
  2. 当开始在 textFields 中输入时,textFields 会向上滚动,超出可视区域.
  3. 当 firstResponder 辞职(键盘隐藏)时,我无法滚动 contentView.我将按照上面指定的顺序列出图片截图.

在此尝试之前,我将按钮固定在 ViewController 的视图上,将按钮的底部约束分配给一个变量,当 keyboardDidShow 时,我将键盘大小添加到底部约束,从而将按钮向上发射到键盘上方.然而,stackoverflower 表示这种方法容易出现错误:触发viewDidLoad时,按钮不显示

当开始在 textFields 中输入时

键盘隐藏时

 想要的结果

解决方案

所以我认为您很接近,我不确定这是代码问题还是自动布局问题.我的猜测是您会抱怨您的滚动视图不知道内容大小,因此我将涵盖两者.

此外,您的按钮必须位于我第一次添加的视图容器下方,并且需要与滚动视图分开处理.不要把它放在滚动视图中.

除了将 50 或任何内容添加到将包含滚动视图的视图的布局底部之外,这些方法都在下面工作.下面的方法也将有助于进行编辑

自动布局:首先对于只占用页面的表单,我喜欢首先向故事板添加视图并固定顶部布局指南(按钮所需的任何空间),左侧和右侧.然后我将我的 ScrollView(将滚动视图固定到该视图)添加到刚刚添加的视图中.然后接下来我将我的内容视图添加到滚动视图.现在我把它固定在滚动视图上.你会看到自动布局仍然不愉快.那么为什么是第一个视图以及如何解决这个问题.我从 contentView 拖到持有滚动视图的视图并选择相等的高度和相等的宽度.现在你不会有自动布局对你尖叫.注意:这适用于您想要填充第一个视图大小但允许它滚动以避免键盘的内容.看图片

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

现在将您的 NEXT 按钮添加到包含所有内容的视图下方的故事板.该按钮必须用 0 固定在主视图的底部才能粘在键盘上.现在看起来像这样.

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

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

最后是代码.当我循环遍历子视图以设置委托时,其中一些您需要替换文本字段变量.我还在向上滚动中添加了填充.您应该将 deRegister 移至 deint().查看我的代码,最后您可能想要滚动键盘上的滚动视图将出现而不是确实出现,但我没有改变这一点.

导入 UIKit类视图控制器:UIViewController,UITextFieldDelegate {//添加到故事板中.删除了代码@IBOutlet 弱变量 nextButton:UIButton!@IBOutlet 弱变量滚动视图:UIScrollView!@IBOutlet 弱变量 contentView:UIView!覆盖 func viewDidLoad() {super.viewDidLoad()//每当显示/隐藏键盘时调用registerForKeyboardNotifications()//当识别单次或多次点击时,调用DismissKeyboardvar tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "DismissKeyboard")contentView.addGestureRecognizer(点击)//禁用滚动弹跳scrollView.bounces = false//用你的文本框替换对于 self.contentView.subviews 中的 subs{如果 subs 是 UITextField{打印(设置")(subs as! UITextField).delegate = self}}}//当点击被识别时调用此函数.func DismissKeyboard(){contentView.endEditing(真)}//在回车键点击时停止编辑.func textFieldShouldReturn(_ textField: UITextField) ->布尔{textField.resignFirstResponder()返回真}//编辑下一个按钮弱变量 activeField:UITextField?func keyboardDidShow(_ 通知:通知) {//当编辑文本字段时,抬起键盘上方的按钮如果让activeField = self.activeField,让keyboardSize =(notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as?NSValue)?.cgRectValue {//20 insets 和 offset 只是填充让 contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom:keyboardSize.height + 20 + nextButton.bounds.height,右:0.0)self.scrollView.contentInset = contentInsetsvar aRect = self.view.frameaRect.size.height -= keyboardSize.height让bottomPoint = CGPoint(x: activeField.frame.origin.x, y:activeField.frame.origin.y)如果 aRect.contains(bottomPoint){让滚动点 = CGPoint(x: 0.0, y: bottomPoint.y - keyboardSize.height - 20 - nextButton.bounds.height)scrollView.setContentOffset(滚动点,动画:真)}}}func keyboardWillHide(_ 通知:通知) {让 contentInsets = UIEdgeInsets.zeroself.scrollView.contentInset = contentInsetsself.scrollView.scrollIndicatorInsets = contentInsets}//跟踪正在编辑的文本字段以确保该字段在键盘弹出时可见func textFieldDidBeginEditing(_ textField: UITextField) {self.activeField = textField}func textFieldDidEndEditing(_ textField: UITextField, 原因: UITextFieldDidEndEditingReason) {self.activeField = nil}//注册键盘通知func registerForKeyboardNotifications() {NotificationCenter.default.addObserver(self, selector:#selector(keyboardDidShow),名称:NSNotification.Name.UIKeyboardDidShow,对象:nil)NotificationCenter.default.addObserver(self, selector:#selector(keyboardWillHide),名称:NSNotification.Name.UIKeyboardWillHide,对象:nil)}//删除键盘观察者func deregisterFromKeyboardNotifications() {NotificationCenter.default.removeObserver(self, name:NSNotification.Name.UIKeyboardDidShow,对象:nil)NotificationCenter.default.removeObserver(self, name:NSNotification.Name.UIKeyboardWillHide,对象:nil)}去初始化{//注销键盘通知deregisterFromKeyboardNotifications()}}//课程结束

现在又进了一步.我们必须处理按钮的向上移动.您可以将bottomconstraint 子类化来处理它,而不是仅仅杀死这个控制器并在其中进行更多处理.(请确保不要在底部添加顶部约束.)这是要放入项目的约束.

导入 UIKit类避免约束:NSLayoutConstraint {私有变量偏移:CGFloat = 0私有变量键盘可见高度:CGFloat = 0覆盖公共 funcawakeFromNib() {super.awakeFromNib()偏移量 = 常数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)}去初始化{NotificationCenter.default.removeObserver(self)}//标记:通知func keyboardWillShowNotification(_通知:通知){如果让 userInfo = notification.userInfo {如果让 frameValue = userInfo[UIKeyboardFrameEndUserInfoKey] 作为?NSValue {让 frame = frameValue.cgRectValuekeyboardVisibleHeight = frame.size.height}self.updateConstant()switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as?NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as?NSNumber){case let (.some(duration), .some(curve)):让选项 = UIViewAnimationOptions(rawValue: curve.uintValue)UIView.animate(withDuration: TimeInterval(duration.doubleValue),延迟:0,选项:选项,动画:{UIApplication.shared.keyWindow?.layoutIfNeeded()返回},完成:{完成})默认:休息}}}func keyboardWillHideNotification(_通知:NSNotification){键盘可见高度 = 0self.updateConstant()如果让 userInfo = notification.userInfo {switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as?NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as?NSNumber){case let (.some(duration), .some(curve)):让选项 = UIViewAnimationOptions(rawValue: curve.uintValue)UIView.animate(withDuration: TimeInterval(duration.doubleValue),延迟:0,选项:选项,动画:{UIApplication.shared.keyWindow?.layoutIfNeeded()返回},完成:{完成})默认:休息}}}功能更新常量(){self.constant = offset + keyboardVisibleHeight}}

将此添加到项目中的文件中.然后你所要做的就是在故事板中你的按钮的底部约束将其更改为这个子类.只要确保没有顶部约束.持有滚动视图的视图需要对主视图有一个底部约束,而不是为按钮提供足够空间的按钮.运行项目并享受.如果这个解释还不够,请参阅测试项目的链接.

I have created a form with 4 fields and one button. The view hierarchy looks like this: Main UIVIew, View (renamed contentView), on top of contentView I have 4 fields and 1 programmatically created button.

  1. When viewDidLoad is triggered, the button is not scrolled up so it can be visible in contentView.
  2. When starting to type in textFields, the textFields are scrolled up, outside of the viewable area.
  3. When firstResponder resigns (keyboard hidden) I cannot scroll the contentView. I will list the pictures screenshots in the order specified above.

Prior to this attempt, I pined the button on the ViewController's view, assigned the button's bottom constraint to a variable and when keyboardDidShow, I was adding the keyboard size to the bottom constraint, thus shooting the button up above the keyboard. However a stackoverflower said this method was prone to bugs: Move button when keyboard appears swift

I have followed this tutorial, but I don’t get same result. https://spin.atomicobject.com/2014/03/05/uiscrollview-autolayout-ios/
Given that Iphone has different screen sizes, please advise the best approach.

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

When viewDidLoad is triggered,button does not show

When starting to type in textFields

When keyboard is hidden

 Desired result

解决方案

So I think you are close and I am not sure if it is a code issue or an autolayout issue. My guess is you are getting complaints about your scrollview not know the content size so I will cover both.

EDIT: Also you button will have to be below the view container that I first add and will need to be handled separately from the scrollview. Do not put it in the scrollview.

The methods work below except add 50 or whatever to the layout bottom for the view that will hold the scrollview. Also the method below will help make edits

Autolayout: First for forms that will only take up on page I like to start by adding a view to the storyboard and pin in top layout guide, (whatever space you need for button), left and right. Then I add my ScrollView(pin the scrollview to that view) to the view just added.Then next I add my content view to the scrollview. Now I pin this to the scrollview. You will see that autolayout is still not happy. So why the first view and how to fix this. I drag from the contentView to the view holding the scrollview and choose equal heights and equal widths. Now you will not have auto layout screaming at you. Note:This works for content you want to fill the first view size but allow it to scroll to avoid the keyboard. See images

After adding this equal heights I can continue with the storyboard. I set the textfields up. The bottom textfield you may or may not want to pin it to the bottom but if you do make it >= yourNumber.

EDIT: Now add your NEXT Button to the storyboard below the view that is holding everything. The button has to be pinned to the bottom of the main view with a 0 to stick to the keyboard. It will now look like this.

Obviously this conflicts slightly with the initial images but all you have to do is increase the space to the bottom layout guide just make sure your button is added to the main view and not the view holding the scrollview. Now connect your button to the controller in an iboutlet. we will need it.

Next make sure you have the right keyboard in your simulator. **Not using hardware keyboard

Finally the code. some of it you would need to substitute your textfield variables as I looped through the subviews to set the delegate. I also added padding to the scroll up. You should move your deRegister to deint(). See my code and finally you might want to scroll the scroll view on keyboard will appear instead of did appear but I did not alter this.

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

Now one more step. We have to handle the move up of the button. Instead of just killing this controller and putting more handling in it you can subclass the bottomconstraint to handle it. (Just make sure not to add a top constraint to the bottom.) Here is a constraint to drop in your project.

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
    }

}

Add this to a file in your project. Then all you have to do is on the bottom constraint for your button in story board change it to this subclass. Just make sure there is no top constraint. The view holding the scrollview needs to have a bottom constraint to the main view and not the button with enough space for the button. Run the project and enjoy. See link for test project if this explanation is not enough. https://www.dropbox.com/s/ir5x324mvhhne64/ScrollView.zip?dl=0

这篇关于键盘向上移动时如何使文本字段保持原位?迅速的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆