已显示键盘时点击第二个UITextField,内容偏移异常
Hey John, let's break down these keyboard-related headaches you're facing and tackle them one by one!
1. Why does keyboardWillChange fire twice when switching text fields?
This is actually a common quirk on iOS—when you tap a second text field without dismissing the keyboard first, the system technically triggers a "keyboard adjust" (not a full dismiss/reappear, but a frame update) which can fire the notification twice. Here's how to fix it:
Check for duplicate notification registration: Make sure you're not registering the keyboard notification multiple times (e.g., in
viewWillAppearwithout removing it inviewWillDisappear). Example of the correct pattern:override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillChange), name: UIResponder.keyboardWillChangeFrameNotification, object: nil ) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver( self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil ) }Filter callbacks by active text field: Track which text field is currently focused, and only process the keyboard update if it relates to that field. Add a delegate to your text fields:
var activeTextField: UITextField? func textFieldDidBeginEditing(_ textField: UITextField) { activeTextField = textField } @objc func keyboardWillChange(_ notification: Notification) { guard let userInfo = notification.userInfo, let keyboardEndFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect, activeTextField != nil else { return } // Only proceed if this update is for the currently active field let keyboardHeight = view.bounds.height - keyboardEndFrame.origin.y // Your layout logic here }
2. Why is my calculated frame being ignored by Xcode?
This almost always boils down to Auto Layout overriding your manual frame changes. If your view uses constraints (which it should for modern iOS apps), directly setting frame values will get overwritten the next time Auto Layout recalculates the layout. Here's the fix:
Use constraints instead of manual frame edits:
- Add a bottom constraint to your text field (connect it to the parent view's bottom, or a safe area bottom) and keep a reference to it:
@IBOutlet weak var textFieldBottomConstraint: NSLayoutConstraint! - Update this constraint's
constantvalue in your keyboard callback, and trigger layout updates in an animation block:@objc func keyboardWillChange(_ notification: Notification) { guard let userInfo = notification.userInfo, let keyboardEndFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } let keyboardHeight = view.bounds.height - keyboardEndFrame.origin.y // Adjust the constraint to account for keyboard height + some padding textFieldBottomConstraint.constant = keyboardHeight + 16 // Match the keyboard's animation timing let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0.25 UIView.animate(withDuration: duration) { self.view.layoutIfNeeded() } }
- Add a bottom constraint to your text field (connect it to the parent view's bottom, or a safe area bottom) and keep a reference to it:
Disable automatic scroll view insets (if using scroll views):
If your page uses aUIScrollView,UITableView, orUICollectionView, iOS automatically adjusts content insets to avoid keyboard overlap—this can clash with your custom layout. Turn it off:// For iOS 11+ scrollView.contentInsetAdjustmentBehavior = .never // For iOS 10 and below automaticallyAdjustsScrollViewInsets = falseCheck image constraints: Since your page includes images, make sure their constraints (like fixed height/width, or aspect ratio) aren't causing layout conflicts that force the system to reset your text field positions. Double-check for ambiguous constraints in the Xcode debug console.
Quick Bonus Tip
Don't forget to reset your layout when the keyboard dismisses:
func textFieldDidEndEditing(_ textField: UITextField) { textFieldBottomConstraint.constant = 16 // Reset to your original padding UIView.animate(withDuration: 0.25) { self.view.layoutIfNeeded() } activeTextField = nil }
Let me know if you need to share a snippet of your layout code or constraints—I can help dig deeper into any edge cases!
内容的提问来源于stack exchange,提问作者John Williams

