SwiftUI中keyboardWillShowNotification重复触发两次的问题求助
解决SwiftUI中键盘通知
keyboardWillShow触发两次的问题 问题原因
你的代码存在三个核心问题导致通知重复触发:
- 观察者实例重复创建:使用
@ObservedObject声明KeyboardObserver,当MovingTextfield视图因状态更新重新渲染时,会生成新的KeyboardObserver实例。 - 重复注册通知:
onAppear可能被多次调用,每次调用都会执行setNotification添加新的观察者,同一个键盘通知会被多个观察者实例接收,导致keyboardWillShow触发多次。 - 未清理旧观察者:没有在对象销毁时移除观察者,旧实例的订阅未被取消,也会导致重复响应通知。
修复方案
1. 用@StateObject替代@ObservedObject
@StateObject会确保观察者实例在视图整个生命周期内只初始化一次,避免重复创建。
2. 改用Combine方式监听通知(推荐)
Combine的订阅会自动管理生命周期,无需手动移除观察者,更符合SwiftUI的响应式编程风格。
修改后的代码
KeyboardObserver类
import SwiftUI import Combine class KeyboardObserver: ObservableObject { @Published var activeState: Bool = false private var cancellables = Set<AnyCancellable>() init() { // 监听键盘弹出通知 NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification) .sink { [weak self] _ in guard let self = self else { return } print("before appear state: \(self.activeState)") self.activeState = true print("after appear state: \(self.activeState)") } .store(in: &cancellables) // 监听键盘收起通知 NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification) .sink { [weak self] _ in guard let self = self else { return } self.activeState = false print("disappear state: \(self.activeState)") } .store(in: &cancellables) } }
MovingTextfield视图
struct MovingTextfield: View { @State var active: Bool = false @StateObject var observer = KeyboardObserver() // 替换为@StateObject @Binding var keywordValue: String var body: some View { VStack{ Text("\(active)") Text("\(observer.activeState)") HStack{ Spacer() TextField("\(Text("text input").font(.title))", text: $keywordValue) .foregroundStyle(.black).font(.title) .multilineTextAlignment(.center) .disableAutocorrection(true) Spacer() } } // 移除原onAppear中的setNotification调用 } }
ContentView保持不变
import SwiftUI struct ContentView: View { @State var keywordValue = "ABCDEFG" var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") MovingTextfield(keywordValue: $keywordValue) } .padding() } } #Preview { ContentView() }
额外说明
如果坚持使用selector方式监听通知,需要在KeyboardObserver的deinit方法中移除观察者,同时确保setNotification只被调用一次:
class KeyboardObserver: ObservableObject { @Published var activeState: Bool = false init() { setNotification() } deinit { NotificationCenter.default.removeObserver(self) } func setNotification(){ 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 func keyboardWillShow(notification: Notification){ print("before appear state: \(activeState)") activeState = true print("after appear state: \(activeState)") } @objc func keyboardWillHide(notification: Notification){ activeState = false print("disappear state: \(activeState)") } }
内容的提问来源于stack exchange,提问作者rainbowfart
相关产品推荐
相关产品推荐

