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

iOS14下SwiftUI TextField随交互式键盘收起同步下移问题

iOS14 SwiftUI TextField 交互式收键盘同步下移解决方案

核心问题分析

原生SwiftUI TextField在iOS14对键盘交互式拖拽的连续帧变化响应不足,单纯依赖键盘显示/隐藏通知只能处理一次性的位置变更,无法跟随拖拽过程同步更新布局。


具体解决思路

1. 封装原生UITextField(替代SwiftUI原生TextField)

通过UIViewRepresentable封装原生UITextField,能获取键盘帧变化的实时回调,精准响应交互式拖拽:

struct CustomTextField: UIViewRepresentable {
    @Binding var text: String
    @Binding var bottomInset: CGFloat

    func makeUIView(context: Context) -> UITextField {
        let tf = UITextField()
        tf.borderStyle = .roundedRect
        tf.delegate = context.coordinator
        // 监听键盘帧变化通知(关键:覆盖交互式拖拽的连续变化)
        NotificationCenter.default.addObserver(
            context.coordinator,
            selector: #selector(Coordinator.handleKeyboardFrameChange(_:)),
            name: UIWindow.keyboardWillChangeFrameNotification,
            object: nil
        )
        return tf
    }

    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        let parent: CustomTextField

        init(parent: CustomTextField) {
            self.parent = parent
        }

        @objc func handleKeyboardFrameChange(_ notification: Notification) {
            guard let userInfo = notification.userInfo,
                  let endFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect,
                  let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval,
                  let curveRaw = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt else { return }

            // 计算有效键盘高度(扣除安全区域底部)
            let screenBounds = UIScreen.main.bounds
            let keyboardOverlap = endFrame.intersection(screenBounds).height
            let safeBottom = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0
            let targetInset = max(0, keyboardOverlap - safeBottom)

            // 同步键盘动画曲线及时长,保证布局跟手
            UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curveRaw)) {
                self.parent.bottomInset = targetInset
            }
        }
    }
}

2. 用ScrollView承载并绑定动态内边距

将TextField放在ScrollView中,通过绑定的bottomInset动态调整contentInset和scrollIndicatorInsets,让ScrollView自动跟随键盘拖拽滚动:

struct ContentView: View {
    @State private var inputText = ""
    @State private var keyboardBottomInset: CGFloat = 0

    var body: some View {
        ScrollView {
            VStack(spacing: 20) {
                // 占位内容,模拟页面长度
                ForEach(0..<10) { idx in
                    Text("行 \(idx + 1)")
                        .frame(maxWidth: .infinity, minHeight: 44)
                        .background(Color.gray.opacity(0.1))
                }
                // 自定义TextField
                CustomTextField(text: $inputText, bottomInset: $keyboardBottomInset)
                    .frame(height: 44)
            }
            .padding()
        }
        .contentInset(EdgeInsets(top: 0, leading: 0, bottom: keyboardBottomInset, trailing: 0))
        .scrollIndicatorInsets(EdgeInsets(top: 0, leading: 0, bottom: keyboardBottomInset, trailing: 0))
    }
}

3. 关键注意事项

  • 放弃键盘显示/隐藏通知:UIKeyboardWillShow/HideNotification只触发两次(显示/完全隐藏),无法覆盖拖拽过程中的连续变化,必须用UIKeyboardWillChangeFrameNotification。
  • 动画同步:严格使用键盘通知返回的duration和animationCurve,避免布局动画与键盘动画脱节。
  • iOS14适配:直接通过UIApplication.shared.windows.first获取窗口计算安全区域,iOS14无需适配connectedScenes。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 15:05:31