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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:05:21