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

Interactive Keyboard Dismiss失效:TextField随键盘交互位移异常

解决SwiftUI中Overlay输入框与键盘交互不同步的问题

问题分析

核心问题在于Overlay层的输入框未绑定键盘高度的动态变化:仅给ScrollView添加了底部内边距适配键盘,但输入框的位置完全独立于键盘高度,导致交互关闭键盘时,输入框无法跟随键盘的实时高度变化同步移动,直到键盘完全消失才会回到原位。同时原代码未利用键盘通知中的系统动画参数,动画曲线和时长与系统键盘不匹配,进一步加剧了视觉错位。

修复方案

  1. 将Overlay输入框的位置与keyboardHeight绑定,通过offset实现跟随键盘移动
  2. 利用键盘通知中的系统动画参数,让UI动画与键盘动画完全同步
  3. 给Overlay层添加对应动画,确保输入框移动过程平滑

修改后的完整代码

import SwiftUI
import Combine

struct InteractiveTopKeyboard: View {
    @State var text = ""
    @State private var keyboardHeight: CGFloat = 0
    @State private var isKeyboardVisible = false
    private let keyboardWillShow = NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)
    private let keyboardWillHide = NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)
    
    var body: some View {
        NavigationStack {
            VStack {
                ScrollView {
                    LazyVStack {
                        ForEach(0..<20, id: \.self) { _ in
                            RoundedRectangle(cornerRadius: 25.0)
                                .foregroundStyle(.blue)
                                .frame(height: 300)
                                .rotationEffect(.degrees(180.0))
                                .scaleEffect(x: -1, y: 1, anchor: .center)
                        }
                    }
                }
                .rotationEffect(.degrees(180.0))
                .scaleEffect(x: -1, y: 1, anchor: .center)
                .scrollDismissesKeyboard(.interactively)
                .padding(.bottom, keyboardHeight)
                .animation(.easeOut(duration: 0.3), value: keyboardHeight)
            }
            .ignoresSafeArea()
            .onAppear {
                addKeyboardObservers()
            }
            .onDisappear {
                removeKeyboardObservers()
            }
            .overlay(alignment: .bottom) {
                HStack {
                    Spacer()
                    TextField("Enter text", text: $text)
                        .padding(.horizontal)
                        .padding(.vertical, 8)
                        .background(.gray)
                        .clipShape(Capsule())
                    Spacer()
                }
                .padding(.top, 5)
                .padding(.bottom, 30)
                .background(.ultraThinMaterial)
                // 绑定输入框位置到键盘高度,实现同步移动
                .offset(y: -keyboardHeight)
                // 给输入框添加动画,保证移动平滑
                .animation(.easeOut(duration: 0.3), value: keyboardHeight)
            }
        }
    }
    
    private func addKeyboardObservers() {
        keyboardWillShow
            .merge(with: keyboardWillHide)
            .sink { notification in
                if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
                    // 获取系统键盘的动画参数,让UI动画完全匹配
                    let animationDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0.3
                    
                    withAnimation(Animation.easeOut(duration: animationDuration)) {
                        isKeyboardVisible = notification.name == UIResponder.keyboardWillShowNotification
                        keyboardHeight = isKeyboardVisible ? keyboardFrame.height : 0
                    }
                }
            }
            .store(in: &cancellables)
    }
    
    private func removeKeyboardObservers() {
        cancellables.removeAll()
    }
    
    @State private var cancellables: Set<AnyCancellable> = []
}

关键修改点说明

  1. 输入框位置绑定:
    添加.offset(y: -keyboardHeight),让输入框在键盘弹出时向上移动对应高度,键盘收起时自动回到原位,完全跟随键盘高度变化。

  2. 同步系统动画:
    从键盘通知中提取动画时长参数,确保UI动画的时长与系统键盘完全一致,避免视觉上的错位感。

  3. Overlay层动画:
    给输入框添加与ScrollView一致的动画绑定,保证输入框移动过程平滑过渡,和键盘的交互体验统一。

内容的提问来源于stack exchange,提问作者Ahmed Zaidan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 08:51:09