文章目录
- [📘 iOS / SwiftUI 输入法(键盘)处理总结](#📘 iOS / SwiftUI 输入法(键盘)处理总结)
- 一、问题背景
- 二、输入框切换闪烁问题
📘 iOS / SwiftUI 输入法(键盘)处理总结
一、问题背景
在 iOS / SwiftUI 开发中,常见输入法问题:
- 输入框切换时键盘闪烁
- 键盘弹出/收起动画不自然
- 无法像 Android WindowInsetsAnimation 一样平滑跟随
二、输入框切换闪烁问题
❌ 错误原因
swift
focusedField = nil
focusedField = .password
解决办法
swift
enum Field {
case username
case password
}
@FocusState private var focusedField: Field?
TextField("用户名", text: $username)
.focused($focusedField, equals: .username)
SecureField("密码", text: $password)
.focused($focusedField, equals: .password)
❗ 注意
不要先设为 nil
不要手动关闭键盘
使用系统焦点切换
键盘动画(类似 Android Insets)
uikit
swift
@objc func keyboardWillChangeFrame(_ notification: Notification) {
guard let userInfo = notification.userInfo else { return }
let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0.25
let curveRaw = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? 0
let curve = UIView.AnimationOptions(rawValue: curveRaw << 16)
let frame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect ?? .zero
let keyboardHeight = UIScreen.main.bounds.height - frame.origin.y
UIView.animate(withDuration: duration, delay: 0, options: curve) {
self.bottomConstraint.constant = keyboardHeight
self.view.layoutIfNeeded()
}
}
swiftUI
- 推荐
swift
.safeAreaInset(edge: .bottom) {
Color.clear.frame(height: 0)
}
- combine
swift
class KeyboardObserver: ObservableObject {
@Published var height: CGFloat = 0
private var cancellables = Set<AnyCancellable>()
init() {
NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)
.map { notification -> CGFloat in
let frame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect ?? .zero
return UIScreen.main.bounds.height - frame.origin.y
}
.assign(to: \.height, on: self)
.store(in: &cancellables)
}
}
struct ContentView: View {
@StateObject var keyboard = KeyboardObserver()
var body: some View {
VStack {
Spacer()
TextField("输入", text: .constant(""))
.textFieldStyle(.roundedBorder)
}
.padding(.bottom, keyboard.height)
.animation(.easeOut(duration: 0.25), value: keyboard.height)
}
}