首页 > 解决方案 > 当被键盘覆盖时,TextField 向上移动并没有保持原位

问题描述

我正在尝试解决常见问题,以便在 TextField 被键盘覆盖时向上移动。基本上,我遵循了移动位于键盘下方的内容中的 Apple 文档

但我没有使用 ScrollView,在主视图中只有两个 TextFiled。

当它被键盘覆盖时,下一个会向上移动,并且在 iPhone 8 或 SE 上完美运行(具有锐利的屏幕角,主视图和 SafeArea 相同)。

我在 iPhone X、XS 或 11 上遇到问题(那些带有 Face ID 缺口和圆角屏幕角的设备,其中安全区域和主视图不一样)。

在这些 iPhone 上,TextField 最初向上移动到所需位置,但在触摸键盘后(键入字母后),它会立即向下跳一些像素并再次隐藏在键盘后面

这是我正在处理的代码:

//
//  ViewController.swift
//  TextFields
//
//  Created by Manoli on 24/04/2020.
//  Copyright © 2020 macForce. All rights reserved.
//

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var upperTextField: UITextField!
    @IBOutlet weak var lowerTextField: UITextField!

    var activeTextField: UITextField?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        upperTextField.delegate = self
        lowerTextField.delegate = self

        registerForKeyboardNotifications()
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        activeTextField = nil
        textField.resignFirstResponder()
        return true
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {
        activeTextField = textField
    }

    func registerForKeyboardNotifications() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)

        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    @objc func keyboardWillShow(_ notification: NSNotification) {

        let keyboardSpacing: CGFloat = 8.0 // Standard value for spacing between keyboard and textfield

        guard let userInfo = notification.userInfo,
            let keyboardFrameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
            else { return }

        let keyboardFrame = keyboardFrameValue.cgRectValue

        guard let activeTextFieldFrame = activeTextField?.frame else { return }

        let positionShift = activeTextFieldFrame.origin.y + activeTextFieldFrame.height - keyboardFrame.origin.y + keyboardSpacing

        if positionShift > 0 {
            view.frame.origin.y = -positionShift
        }
    }

    @objc func keyboardWillHide(_ notification: NSNotification) {
        view.frame.origin.y = 0
    }
}

在这里,我有GitHub 上整个项目的链接。

有谁知道为什么这只会在某些类型的 iPhone 屏幕上出现问题?我在这段代码中使用的想法有什么问题吗?

非常感谢任何建议或建议,祝大家有美好的一天。

更新:

我发现如果 TextFields 的垂直约束是针对 Superview 而不是针对 Safe Area 设置的,它可以正常工作(也在 iPhone X 及更高版本上)。所以我现在已经相应地更新了 GitHub 上的项目。(我唯一不喜欢的是对 Superview 的垂直约束不是对称的,它们在 iPhone SE 和 8 及以下看起来不太正确。)

更新:

现在尝试使用容器视图底部约束和安全区域差异的不同方法来计算在usingConstraints分支中向上移动的 TextFiled。它适用于所有可能的屏幕类型,但不能使用键盘进行动画处理。

ToDo:仍然需要弄清楚如何根据键盘对这个约束变化进行动画处理。

标签: iosswiftkeyboardtextfield

解决方案


func textFieldShouldBeginEditing(_ textField: UITextField) 
{
    activeTextField = textField
}

func showLoginKeyBoard()
{
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
}

 @objc func keyboardWillShow(notification: NSNotification)
{
    if activeTextField == upperTextField
    {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        {
            if self.view.frame.origin.y == 0
            {
                self.view.frame.origin.y -= keyboardSize.height
            }
        }
    }

    if activeTextField == lowerTextField
    {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        {
            if self.view.frame.origin.y == 0
            {
                self.view.frame.origin.y -= keyboardSize.height + 50
            }
        }
    }
}

func hideLoginKeyboard()
{
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

    let tap: UITapGestureRecognizer = UITapGestureRecognizer(
        target: self,
        action: #selector(UIViewController.keyboardWillHide))
    tap.cancelsTouchesInView = false
    view.addGestureRecognizer(tap)
    self.view.endEditing(true)
}

@objc func keyboardWillHide(notification: NSNotification)
{
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1), execute:
        {
            if self.view.frame.origin.y != 0
            {
                UIView.animate(withDuration: 0.3, delay: 0.0, animations:
                    {
                        self.view.frame.origin.y = 0
                })
            }
    })
}

func deregisterFromKeyboardNotifications()
{
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}

override func viewDidDisappear(_ animated: Bool)
{
    deregisterFromKeyboardNotifications()
}

推荐阅读