SwiftUI中如何隐藏键盘并显示新视图时保持.toolbar可见?
实现点击按钮隐藏键盘并填充原键盘空间的交互(仿Notion)
核心思路
通过监听键盘的显示/隐藏状态,动态调整底部视图的布局约束(或位置),让它在键盘弹出时被顶起,键盘隐藏时自动填充原键盘占据的空间。点击按钮时主动收起键盘,借助系统键盘的动画参数实现平滑过渡,和Notion的效果对齐。
iOS(Swift)实现示例
1. 准备UI元素
- 输入框(
UITextField/UITextView) - 触发按钮(
UIButton) - 用于填充的底部容器视图(
UIView) - 给底部容器视图添加底部约束(连接到父视图底部),并将该约束关联到代码中
2. 代码实现
import UIKit class ViewController: UIViewController { @IBOutlet weak var inputTextField: UITextField! @IBOutlet weak var actionButton: UIButton! @IBOutlet weak var bottomContainerView: UIView! @IBOutlet weak var bottomContainerBottomConstraint: NSLayoutConstraint! private var keyboardHeight: CGFloat = 0 override func viewDidLoad() { super.viewDidLoad() setupKeyboardObservers() actionButton.addTarget(self, action: #selector(triggerHideKeyboardAndFillSpace), for: .touchUpInside) } private func setupKeyboardObservers() { NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil ) } @objc private func keyboardWillShow(_ notification: Notification) { guard let userInfo = notification.userInfo, let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect, let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval, let curveRawValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else { return } keyboardHeight = keyboardFrame.height bottomContainerBottomConstraint.constant = keyboardHeight UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curveRawValue)) { self.view.layoutIfNeeded() } } @objc private func keyboardWillHide(_ notification: Notification) { guard let userInfo = notification.userInfo, let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval, let curveRawValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else { return } bottomContainerBottomConstraint.constant = 0 UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curveRawValue)) { self.view.layoutIfNeeded() } } @objc private func triggerHideKeyboardAndFillSpace() { // 主动收起键盘 inputTextField.resignFirstResponder() // 键盘隐藏的通知会自动处理底部视图的布局过渡,无需额外操作 } deinit { NotificationCenter.default.removeObserver(self) } }
3. 关键注意点
- 务必在控制器销毁时移除键盘通知监听,避免内存泄漏
- 底部容器视图的
clipsToBounds建议设为true,防止内容超出视图范围 - 如果使用
UITextView,注意设置scrollEnabled为false(若不需要滚动),避免和布局动画冲突
Android(Kotlin)实现思路
- 监听键盘高度:通过
ViewTreeObserver.OnGlobalLayoutListener监听根布局的变化,计算键盘高度 - 动态调整视图位置:为底部填充视图设置
translationY,键盘弹出时上移对应高度,隐藏时恢复为0 - 按钮触发逻辑:点击按钮时调用
InputMethodManager隐藏键盘,同步更新底部视图的位置动画
示例代码片段:
import android.content.Context import android.os.Bundle import android.view.ViewTreeObserver import android.view.inputmethod.InputMethodManager import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { private var keyboardHeight: Int = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setupKeyboardListener() actionButton.setOnClickListener { hideKeyboardAndFillSpace() } } private fun setupKeyboardListener() { rootLayout.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { val r = rootLayout.rootView.windowVisibleDisplayFrame val screenHeight = rootLayout.rootView.height keyboardHeight = screenHeight - r.bottom // 键盘弹出时调整底部视图位置 bottomContainerView.translationY = -keyboardHeight.toFloat() } }) } private fun hideKeyboardAndFillSpace() { val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(inputEditText.windowToken, 0) // 平滑恢复底部视图位置 bottomContainerView.animate().translationY(0f).duration = 250 } }
内容的提问来源于stack exchange,提问作者Justikun
相关产品推荐
相关产品推荐

