Swift 4:键盘弹出不遮挡底部元素且不影响TextView的问题
Hey there! Let’s fix this keyboard layout headache once and for all. I’ve run into exactly the same problem before—shifting the entire view always ends up pushing that top TextView off-screen, which is so annoying. Let’s walk through the proper approach that keeps your top content visible and the keyboard neatly below your bottom buttons, plus troubleshoot the errors you’re seeing with @D.Desai’s implementation.
The key here is to anchor elements properly so you don’t have to move the whole view. Here’s how to set it up in Interface Builder:
- Pin your top
TextViewto the top, leading, and trailing edges of the safe area (not the main view, to avoid notch/home indicator issues). Add a vertical spacing constraint from the TextView’s bottom to the top of your button container (e.g., 16pt). - Wrap your two bottom buttons in a container view (this makes adjusting their position way easier). Pin this container to the bottom, leading, and trailing edges of the safe area, and give it a fixed height (like 60pt) so the buttons stay sized correctly.
- Create an outlet for the container’s bottom constraint (name it something like
buttonContainerBottomConstraint—you’ll need this later).
Instead of shifting everything, we’ll just adjust that bottom constraint when the keyboard pops up. This keeps your top TextView firmly in place. Here’s the code:
First, register for keyboard notifications in viewDidLoad():
override func viewDidLoad() { super.viewDidLoad() // Listen for keyboard show/hide events NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) }
Then add the handler functions to adjust the constraint with smooth animation:
@objc private func keyboardWillShow(_ notification: Notification) { guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } // Account for bottom safe area (like home indicator) so buttons don't overlap let keyboardHeight = keyboardFrame.height - view.safeAreaInsets.bottom buttonContainerBottomConstraint.constant = keyboardHeight // Animate the layout change for a smooth transition UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } } @objc private func keyboardWillHide(_ notification: Notification) { buttonContainerBottomConstraint.constant = 0 UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } }
Don’t forget to clean up the observers when the view controller is deallocated:
deinit { NotificationCenter.default.removeObserver(self) }
If you’re hitting errors with that approach, these are the most common fixes:
- Missing Constraint Outlet: If the code references a constraint you didn’t connect in Interface Builder, Xcode will throw an error. Double-check that you’ve dragged the bottom constraint of your button container to your view controller code to create the outlet.
- Incorrect Selector Signature: Make sure your keyboard handler functions are marked
@objcand accept aNotificationparameter. Xcode will complain if the selector doesn’t match the expected method signature. - Safe Area Oversight: If you’re not subtracting the safe area inset from the keyboard height, the buttons might overlap with the keyboard on devices like the iPhone 14 or later. Add that
- view.safeAreaInsets.bottomline to fix it. - Forgot
layoutIfNeeded(): If you skip calling this inside the animation block, the layout change will happen instantly instead of smoothly, which can cause layout warnings or unexpected jumps.
If your TextView has lots of content (or you have other elements above it), wrap everything in a UIScrollView. Then adjust the scroll view’s inset instead of the button constraint:
@objc private func keyboardWillShow(_ notification: Notification) { guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } let keyboardHeight = keyboardFrame.height - view.safeAreaInsets.bottom scrollView.contentInset.bottom = keyboardHeight scrollView.scrollIndicatorInsets.bottom = keyboardHeight } @objc private func keyboardWillHide(_ notification: Notification) { scrollView.contentInset.bottom = 0 scrollView.scrollIndicatorInsets.bottom = 0 }
This way, users can scroll to see all content without anything getting pushed off-screen.
内容的提问来源于stack exchange,提问作者ken

