You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)实现思路

  1. 监听键盘高度:通过ViewTreeObserver.OnGlobalLayoutListener监听根布局的变化,计算键盘高度
  2. 动态调整视图位置:为底部填充视图设置translationY,键盘弹出时上移对应高度,隐藏时恢复为0
  3. 按钮触发逻辑:点击按钮时调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 08:49:53