macOS 14中TextEditor与垂直轴TextField的.focused修饰器失效问题
macOS 14下SwiftUI TextEditor与垂直轴TextField的.focused修饰器失效问题
核心结论
这是macOS 14(Sonoma)的已知兼容性bug,并非Apple有意调整的API行为。该功能在macOS 13中完全正常,目前已有大量开发者反馈此问题,官方尚未发布修复补丁,但大概率会在后续系统更新中解决。
问题复现验证
你提供的两段代码均可复现问题:
- 绑定
@FocusState的TextEditor,焦点变化时isEmailFocused不会更新 - 使用
axis: .vertical的TextField同样存在该问题,onChange(of: isEmailFocused)无法触发预期的打印或数据保存逻辑
临时解决方案
针对你核心需求——保证多行文本输入的数据保存,提供以下几种可行方案:
方案1:直接监听文本变化(最可靠)
如果数据保存不需要依赖焦点失活事件,直接监听文本的onChange事件即可,完全绕过焦点判断的问题:
struct ContentView: View { @State private var email = "" var body: some View { NavigationView { Form { TextField("Address", text: $email, axis: .vertical) TextEditor(text: $email) } .navigationTitle("Sign in") .onChange(of: email) { newValue in // 可添加防抖逻辑避免频繁保存 saveInput(newValue) } } } private func saveInput(_ content: String) { // 替换为你的数据保存逻辑 print("Saved content: \(content)") } }
方案2:结合AppKit原生API判断焦点
如果必须依赖焦点状态(比如仅在焦点离开时保存),可以通过AppKit的NSResponder来判断文本视图的焦点:
struct ContentView: View { @State private var isEmailFocused: Bool = false @State private var email = "" var body: some View { NavigationView { Form { TextEditor(text: $email) .onTapGesture { DispatchQueue.main.async { isEmailFocused = NSApp.keyWindow?.firstResponder is NSTextView } } } .navigationTitle("Sign in") .onChange(of: isEmailFocused) { newValue in print(newValue) if !newValue { saveInput() } } // 监听窗口失焦,同步更新焦点状态 .onReceive(NotificationCenter.default.publisher(for: NSWindow.didResignKeyNotification)) { _ in isEmailFocused = false } } } private func saveInput() { print("Saved on blur: \(email)") } }
方案3:通过Notification捕获焦点变化
扩展NSTextView,利用通知来传递焦点状态:
// 扩展NSTextView发送焦点通知 extension NSTextView { override open func becomeFirstResponder() -> Bool { let result = super.becomeFirstResponder() NotificationCenter.default.post(name: .textViewFocused, object: self) return result } override open func resignFirstResponder() -> Bool { let result = super.resignFirstResponder() NotificationCenter.default.post(name: .textViewBlurred, object: self) return result } } // 定义通知名 extension Notification.Name { static let textViewFocused = Notification.Name("TextViewFocused") static let textViewBlurred = Notification.Name("TextViewBlurred") } struct ContentView: View { @State private var isEmailFocused: Bool = false @State private var email = "" var body: some View { NavigationView { Form { TextEditor(text: $email) .onReceive(NotificationCenter.default.publisher(for: .textViewFocused)) { _ in isEmailFocused = true } .onReceive(NotificationCenter.default.publisher(for: .textViewBlurred)) { _ in isEmailFocused = false } } .navigationTitle("Sign in") .onChange(of: isEmailFocused) { newValue in print(newValue) if !newValue { saveInput() } } } } private func saveInput() { print("Saved on blur: \(email)") } }
后续建议
- 优先采用方案1,避免依赖不稳定的焦点状态判断
- 关注Apple系统更新日志,等待官方修复该bug
- 若需提交反馈给Apple,可附上你的复现代码,帮助官方加速定位问题
内容的提问来源于stack exchange,提问作者Jack
相关产品推荐
相关产品推荐

