UITextField is partially hidden by Keyboard when opened

好久不见. 提交于 2019-12-11 16:09:21

问题


I am attempting to create a collection of UITextField elements. I'd like the next button on the keyboard to skip to the next field and if that field is hidden from view by the keyboard, scroll it into view.

This is my attempt. It works apart from 1 aspect.

When dismissing the keyboard and then selecting another (or the same) field, the text input is partially hidden by the keyboard (see attached gif).

The meat and potatoes is within the ViewController extension.

class ViewController: UIViewController {

    var activeField: UITextField?
    var lastOffset: CGPoint!
    var keyboardHeight: CGFloat!

    let scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        return scrollView
    }()

    let scrollViewContainer: UIStackView = {
        let view = UIStackView()
        view.axis = .vertical
        view.spacing = 10
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        view.addSubview(scrollView)
        scrollView.addSubview(scrollViewContainer)

        let totalFieldCount = 25

        for i in 1...totalFieldCount {
            let textField = createTextField(self, placeholder: "Field #\(i)", type: .default)

            textField.returnKeyType = i < totalFieldCount ? .next : .done
            textField.tag = i
            scrollViewContainer.addArrangedSubview(textField)
        }

        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            scrollViewContainer.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            scrollViewContainer.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            scrollViewContainer.topAnchor.constraint(equalTo: scrollView.topAnchor),
            scrollViewContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            scrollViewContainer.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
        ])

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

        scrollView.keyboardDismissMode = .interactive
    }

    func createTextField(_ delegate: UITextFieldDelegate?, placeholder: String, type: UIKeyboardType, isSecureEntry: Bool = false) -> UITextField {
        let tf = UITextField(frame: .zero)

        tf.placeholder = placeholder
        tf.backgroundColor = .init(white: 0, alpha: 0.03)
        tf.borderStyle = .roundedRect
        tf.font = .systemFont(ofSize: 14)
        tf.keyboardType = type
        tf.autocapitalizationType = .none
        tf.autocorrectionType = .no
        tf.isSecureTextEntry = isSecureEntry

        tf.heightAnchor.constraint(equalToConstant: 40).isActive = true

        if let delegate = delegate {
            tf.delegate = delegate
        }

        return tf
    }

}

extension ViewController: UITextFieldDelegate {

    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
        activeField = textField
        lastOffset = self.scrollView.contentOffset
        return true
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        let nextTag = textField.tag + 1

           if let nextResponder = textField.superview?.viewWithTag(nextTag) {
               nextResponder.becomeFirstResponder()
           } else {
            activeField?.resignFirstResponder()
            activeField = nil
        }

           return true
    }
}

extension ViewController {
    @objc func keyboardWillShow(notification: NSNotification) {

        guard keyboardHeight == nil else { return }

        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            keyboardHeight = keyboardSize.height

            UIView.animate(withDuration: 0.3, animations: {
                self.scrollView.contentInset.bottom = self.keyboardHeight
            })

            guard let activeField = activeField else { return }

            let distanceToBottom = self.scrollView.frame.size.height - (activeField.frame.origin.y) - (activeField.frame.size.height)
            let collapseSpace = keyboardHeight - distanceToBottom

            guard collapseSpace > 0 else { return }

            UIView.animate(withDuration: 0.3, animations: {
                self.scrollView.contentOffset = CGPoint(x: self.lastOffset.x, y: collapseSpace + 10)
            })
        }
    }

    @objc func keyboardWillHide(notification: NSNotification) {
        UIView.animate(withDuration: 0.3) {
            self.scrollView.contentOffset = self.lastOffset
            self.scrollView.contentInset.bottom = 0
        }

        keyboardHeight = nil
    }
}


回答1:


Replace keyboardFrameBeginUserInfoKey with keyboardFrameEndUserInfoKey



来源:https://stackoverflow.com/questions/58078129/uitextfield-is-partially-hidden-by-keyboard-when-opened

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!