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

Swift中NotificationCenter能否跨目标发送?自定义键盘通知无响应问题

自定义键盘向主App发送通知无响应的问题解决

问题根源

自定义键盘属于App Extension,和主App运行在不同的进程中。NotificationCenter.default是进程内通知中心,只能在同一个进程内传递通知,跨进程无法生效。而系统原生的UIApplication.keyboardDidShowNotification等通知是由系统层面广播的跨进程通知,所以主App能正常接收。

解决方案

方案1:App Groups + UserDefaults(推荐)

通过配置共享的App Groups,让主App和键盘Extension共享UserDefaults,以此传递状态并监听变化。

  1. 配置App Groups

    • 在开发者后台为你的App ID开启App Groups权限,并创建一个唯一的Group标识(比如group.com.yourcompany.yourapp.keyboard)。
    • 在Xcode中,分别给主App和键盘Extension的Signing & Capabilities里添加App Groups,并勾选刚才创建的Group。
  2. 键盘端代码(发送状态)
    在键盘激活/退出时更新共享UserDefaults:

    // 自定义键盘类中
    override func viewDidLoad() {
        super.viewDidLoad()
        // 标记键盘已激活
        let sharedDefaults = UserDefaults(suiteName: "group.com.yourcompany.yourapp.keyboard")
        sharedDefaults?.set(true, forKey: "CustomKeyboardIsActive")
        sharedDefaults?.synchronize()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // 标记键盘已退出
        let sharedDefaults = UserDefaults(suiteName: "group.com.yourcompany.yourapp.keyboard")
        sharedDefaults?.set(false, forKey: "CustomKeyboardIsActive")
        sharedDefaults?.synchronize()
    }
    
  3. 主App端代码(监听状态变化)
    在RootViewController中监听共享UserDefaults的变化:

    // RootViewController中
    override func viewDidLoad() {
        super.viewDidLoad()
        // 监听共享UserDefaults的变更通知
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardStatusChanged(_:)), name: UserDefaults.didChangeNotification, object: nil)
    }
    
    @objc func keyboardStatusChanged(_ notification: Notification) {
        let sharedDefaults = UserDefaults(suiteName: "group.com.yourcompany.yourapp.keyboard")
        let isKeyboardActive = sharedDefaults?.bool(forKey: "CustomKeyboardIsActive") ?? false
        NSLog(TAG, "\(#function) - 自定义键盘状态:\(isKeyboardActive)")
        // 这里执行UI更新或功能暂停/恢复逻辑
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    

方案2:Darwin通知中心(跨进程通知)

使用CFNotificationCenter发送跨进程的Darwin通知,适合简单的状态通知(无法传递复杂数据)。

  1. 键盘端发送通知

    override func viewDidLoad() {
        super.viewDidLoad()
        let notifyCenter = CFNotificationCenterGetDarwinNotifyCenter()
        // 发送键盘激活通知
        CFNotificationCenterPostNotification(notifyCenter, "com.yourcompany.yourapp.CustomKeyboardActive" as CFString, nil, nil, true)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        let notifyCenter = CFNotificationCenterGetDarwinNotifyCenter()
        // 发送键盘退出通知
        CFNotificationCenterPostNotification(notifyCenter, "com.yourcompany.yourapp.CustomKeyboardInactive" as CFString, nil, nil, true)
    }
    
  2. 主App端监听通知

    override func viewDidLoad() {
        super.viewDidLoad()
        let notifyCenter = CFNotificationCenterGetDarwinNotifyCenter()
        // 监听键盘状态变更
        CFNotificationCenterAddObserver(notifyCenter, nil, { (_, _, name, _, _) in
            guard let nameStr = name?.rawValue as? String else { return }
            if nameStr == "com.yourcompany.yourapp.CustomKeyboardActive" {
                NSLog(TAG, "自定义键盘已激活")
                // 处理激活逻辑
            } else if nameStr == "com.yourcompany.yourapp.CustomKeyboardInactive" {
                NSLog(TAG, "自定义键盘已退出")
                // 处理退出逻辑
            }
        }, nil, nil, .deliverImmediately)
    }
    

注意事项

  • 不要尝试用NotificationCenter.default跨Extension和主App通信,进程隔离机制会阻止这种传递。
  • 使用App Groups时,确保主App和Extension的Bundle Identifier前缀一致,且都正确配置了Group权限。
  • Darwin通知是全局广播的,建议使用唯一的通知名称(比如包含你的App Bundle Identifier),避免和其他App冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 21:35:26