问题
on a function I have titled handleKeyboardWillShow, my input textfield is moving higher than expected. It is my intention for the textfield to be pinned to the top of the keyboard view. I have also added the code to the creation of the text field that seems to be causing the issue.
Creation of the textField variable
lazy var inputTextField: UITextField = {
let tf = UITextField()
tf.placeholder = "Enter message..."
tf.translatesAutoresizingMaskIntoConstraints = false
tf.delegate = self
return tf
}()
My view did load function
override func viewDidLoad() {
super.viewDidLoad()
collectionView.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
collectionView.alwaysBounceVertical = true
collectionView.register(ChatMessageCell.self, forCellWithReuseIdentifier: cellId)
collectionView.backgroundColor = .white
collectionView.keyboardDismissMode = .interactive
becomeFirstResponder()
setUpInputComponents()
setUpKeyboardObserver()
}
View will disappear is where I remove the observers
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
//MARK: IMPORTANT FOR SHOWING AND HIDING KEYBOARD TO AVOID MEMORY LEAKS
NotificationCenter.default.removeObserver(self)
}
func setUpKeyboardObserver(){
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func handleKeyboardWillShow(notification:NSNotification){
let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
let keyboardDuration = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue
let safeLayoutGuideBot = UIApplication.shared.keyWindow?.safeAreaInsets.bottom
let height = (keyboardFrame?.height)! - safeLayoutGuideBot!
containerViewBottomAnchor?.constant = -height
UIView.animate(withDuration: keyboardDuration!, animations: {
self.view.layoutIfNeeded()
})
}
@objc func handleKeyboardWillHide(notification:NSNotification){
let keyboardDuration = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue
containerViewBottomAnchor?.constant = 0
UIView.animate(withDuration: keyboardDuration!, animations: {
self.view.layoutIfNeeded()
})
}
This is where the layout for the textView in question and container view are set up.
var containerViewBottomAnchor: NSLayoutConstraint?
func setUpInputComponents(){
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.backgroundColor = .white
view.addSubview(containerView)
containerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
containerViewBottomAnchor = containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
containerViewBottomAnchor?.isActive = true
containerView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
containerView.heightAnchor.constraint(equalToConstant: 50).isActive = true
containerView.addSubview(sendButton)
sendButton.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true
sendButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
sendButton.widthAnchor.constraint(equalToConstant: 80).isActive = true
sendButton.heightAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true
containerView.addSubview(inputTextField)
inputTextField.leftAnchor.constraint(equalToSystemSpacingAfter: containerView.leftAnchor, multiplier: 8).isActive = true
inputTextField.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
inputTextField.rightAnchor.constraint(equalTo: sendButton.leftAnchor).isActive = true
inputTextField.heightAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true
containerView.addSubview(seperatorLineView)
seperatorLineView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
seperatorLineView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
seperatorLineView.widthAnchor.constraint(equalTo: containerView.widthAnchor).isActive = true
seperatorLineView.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
}
回答1:
It looks like you may be double counting the safe area. You should be able to just convert the keyboard's frame into your view's coordinate space and be done. Here's what my keyboard code looks like. Yours will be slightly different because you're using auto layout instead of frame based layout.
@objc func keyboardShown(_ notification: Notification) {
let keyboardEndFrameWindow = (notification as NSNotification).userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue
let keyboardTransitionDuration = (notification as NSNotification).userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber
let keyboardTransitionAnimationCurve = (notification as NSNotification).userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber
let keyboardEndFrameView = self.view.convert(keyboardEndFrameWindow.cgRectValue, from:nil)
var offsetY = keyboardEndFrameView.minY - self.nameField.frame.maxY;
offsetY = min(offsetY, 0);
UIView.animate(withDuration: TimeInterval(keyboardTransitionDuration.doubleValue),
delay: 0.0,
options: UIView.AnimationOptions(rawValue: keyboardTransitionAnimationCurve.uintValue),
animations: { () -> Void in
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: offsetY)
}, completion: nil)
}
回答2:
Perhaps this fix was a hack, but I resolved it by implementing the following
containerViewBottomAnchor?.constant -= (keyboardFrame?.height)! - 100
Statically subtracting the value of 100 from the keyboard frame height.
来源:https://stackoverflow.com/questions/60192971/textview-moving-higher-than-expected-on-keyboardwillshownotification-for-iphone