使用Scroll View移动视图,避免键盘遮挡文本框或文本视图
Hey there, I’ve run into this exact headache before with modal input screens on small iPhones and landscape mode—nothing’s more annoying than the keyboard hiding the text view you need to type in! Let’s walk through how to fix this cleanly using a UIScrollView.
Step 1: Restructure Your Modal Layout
First, we need to wrap all your input elements (location name text field, notes text view, and any buttons) inside a scroll view. Here’s how to set this up in Interface Builder:
- Drag a
UIScrollViewinto your modal view controller’s main view, then pin it to all four edges (top, leading, trailing, bottom) with 0 spacing. - Add a
UIViewinside the scroll view (this is your content container). Pin it to all four edges of the scroll view, and set its width equal to the scroll view’s width—this stops unwanted horizontal scrolling. - Move your text field, text view, and buttons into this content container. Set up vertical constraints as usual (e.g., text field top to container top, text view below the text field, buttons below the text view, container bottom tied to the buttons’ bottom).
Step 2: Handle Keyboard Notifications
We need to adjust the scroll view’s insets when the keyboard appears/disappears so content shifts up to stay visible. Add this code to your modal view controller:
First, register for keyboard updates 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 methods:
@objc private func keyboardWillShow(notification: NSNotification) { guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } // Convert keyboard frame to our view's coordinate system let keyboardHeight = view.convert(keyboardFrame, from: nil).height // Push scroll view content up by keyboard height scrollView.contentInset.bottom = keyboardHeight scrollView.scrollIndicatorInsets.bottom = keyboardHeight } @objc private func keyboardWillHide(notification: NSNotification) { // Reset insets when keyboard disappears scrollView.contentInset.bottom = .zero scrollView.scrollIndicatorInsets.bottom = .zero }
Don’t forget to clean up the observer to avoid memory leaks:
deinit { NotificationCenter.default.removeObserver(self) }
Step 3: Optional (But Smooth): Auto-Scroll to Active Input
To make the experience even better, automatically scroll the active text field/text view into view when the user taps it. Add this extension to your view controller:
extension YourModalViewController: UITextFieldDelegate, UITextViewDelegate { func textFieldDidBeginEditing(_ textField: UITextField) { scrollToActiveElement(textField) } func textViewDidBeginEditing(_ textView: UITextView) { scrollToActiveElement(textView) } private func scrollToActiveElement(_ element: UIView) { let targetFrame = scrollView.convert(element.frame, from: element.superview) scrollView.scrollRectToVisible(targetFrame, animated: true) } }
Just make sure you set the delegate of your text field and text view to the view controller (either in Interface Builder or code).
Step 4: Tweak Modal Size for Landscape
Since your modal is a floating blur window, adjust its size dynamically for landscape to avoid cramped space:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) if traitCollection.verticalSizeClass == .compact { // Landscape mode: use a shorter modal height preferredContentSize = CGSize(width: 350, height: 400) } else { // Portrait mode: your original modal size preferredContentSize = CGSize(width: 350, height: 500) } }
This ensures the modal doesn’t take up too much screen real estate in landscape, and the scroll view has enough room to adjust when the keyboard pops up.
内容的提问来源于stack exchange,提问作者Guy Fisher

