SwiftUI使用NSEvent监听剪贴板
SwiftUI使用NSEvent监听剪贴板

SwiftUI使用NSEvent监听剪贴板

在 SwiftUI 中使用 NSEvent 监听应用级事件(如 ⌘V 剪贴板粘贴),虽然不属于 SwiftUI 的默认机制,但可以通过 AppKit 的 NSEvent监听键盘事件,包括快捷键、剪贴板相关操作等。

SwiftUI使用NSEvent监听代码

1、创建监听器管理类(单例)

import AppKit
import Combine

class KeyboardMonitor: ObservableObject {
    static let shared = KeyboardMonitor()

    private var monitor: Any?
    let pastePublisher = PassthroughSubject<Void, Never>()

    func startMonitoring() {
        guard monitor == nil else { return } // 防止重复添加
        monitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
            if event.modifierFlags.contains(.command),
               event.charactersIgnoringModifiers == "v" {
                self?.pastePublisher.send()
                return nil // 拦截系统 ⌘V,如果你想保留系统粘贴,就 return event
            }
            return event
        }
    }

    deinit {
        if let monitor = monitor {
            NSEvent.removeMonitor(monitor)
        }
    }
}

2、在 SwiftUI 中监听发布器

import SwiftUI

struct PasteListenerView: View {
    @State private var pastedText: String = ""
    @State private var pastedImage: NSImage?

    var body: some View {
        VStack(spacing: 20) {
            Text("按下 ⌘V 来粘贴内容")

            if let image = pastedImage {
                Image(nsImage: image)
                    .resizable()
                    .scaledToFit()
                    .frame(width: 200, height: 200)
            } else {
                Text("粘贴内容:\(pastedText)")
            }
        }
        .padding()
        .onAppear {
            KeyboardMonitor.shared.startMonitoring()
        }
        .onReceive(KeyboardMonitor.shared.pastePublisher) { _ in
            let pb = NSPasteboard.general
            if let imageData = pb.data(forType: .tiff),
               let image = NSImage(data: imageData) {
                pastedImage = image
            } else if let str = pb.string(forType: .string) {
                pastedText = str
            } else {
                pastedText = "剪贴板中无可识别内容"
                pastedImage = nil
            }
        }
    }
}

当用户按下 ⌘V:

如果剪贴板有图片(如在 Finder 复制了一个图片文件或从浏览器复制图片),会展示图片;

如果剪贴板是文字(如网页文本),会显示为字符串;

所有逻辑都运行在 SwiftUI,使用主线程安全更新。

监听器管理类代码解析

1、单例模式KeyboardMonitor

class KeyboardMonitor: ObservableObject {
    static let shared = KeyboardMonitor()

保证应用只创建一次监听器

2、保存NSEvent返回的监听器对象

private var monitor: Any?

添加监听器时 NSEvent.addLocalMonitor… 会返回一个值;最后可以使用这个变量移除监听器(在 deinit 中)。

3、Combine发布者

let pastePublisher = PassthroughSubject<Void, Never>()

这是 Combine 框架中的一个“发布者”;任何人都可以通过 .sink 或 .onReceive 来订阅它;当监听到 ⌘V 时调用 .send(),从而通知订阅者。

4、startMonitoring方法

guard monitor == nil else { return }

防止重复添加监听器。

monitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in

使用NSEvent的addLocalMonitorForEvents方法,监听应用内的键盘事件,只处理 .keyDown 类型,使用使用 [weak self] 防止闭包导致内存泄漏。

if event.modifierFlags.contains(.command),
   event.charactersIgnoringModifiers == "v" {
    self?.pastePublisher.send()
    return nil
}

判断是否是 Command + V,modifierFlags.contains(.command) 检查是否按了 ⌘;charactersIgnoringModifiers == “v” 检查实际按的是字母 v。

如果是,就 .send() 粘贴事件;return nil 表示拦截系统默认的粘贴行为。

也可以改成 return event,这样就不会拦截。

5、取消初始化

if let monitor = monitor {
    NSEvent.removeMonitor(monitor)
}

当 KeyboardMonitor 被释放时自动移除监听器;

这是资源清理的好习惯,防止监听器残留导致潜在 bug。

SwiftUI代码解析

1、初始化键盘事件监听器

.onAppear {
    KeyboardMonitor.shared.startMonitoring()
}

在 .onAppear 中触发,初始化键盘事件监听器。

这一步也可以移到入口文件中:

@main
struct ImageSlimApp: App {
    init() {
        KeyboardMonitor.shared.startMonitoring()
    }
    var body: some Scene {
        // 空 Scene,窗口由 AppDelegate 管理
        Settings {} // 占位,不弹出任何窗口
    }
}

2、.onReceive(…) 监听事件流

.onReceive(KeyboardMonitor.shared.pastePublisher) { _ in
    // 从剪贴板读取数据
}

一旦监听器发出事件,SwiftUI 会自动响应并执行粘贴逻辑。

3、读取剪贴板内容

let pb = NSPasteboard.general
if let imageData = pb.data(forType: .tiff),
   let image = NSImage(data: imageData) {
    pastedImage = image
} else if let str = pb.string(forType: .string) {
    pastedText = str
}

优先读取图像(.tiff 是 macOS 图像通用格式),如果没有图像,再读取纯文本,最后 fallback 到提示文本。

NSEvent监听剪贴板时发挥的作用

虽然 PasteListenerView 中没有直接用 NSEvent,但背后的监听逻辑是:

1、KeyboardMonitor 管理类封装了 NSEvent 的监听行为;

2、一旦 ⌘V 被按下,NSEvent 的回调会触发 .send();

3、PassthroughSubject 发出信号;

4、SwiftUI View .onReceive(…) 接收到事件并响应。

相关文章

1、macOS剪贴板NSPasteboard:https://fangjunyu.com/2025/07/15/macos%e5%89%aa%e8%b4%b4%e6%9d%bfnspasteboard/

2、macOS用户输入事件NSEvent:https://fangjunyu.com/2025/07/04/macos%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5%e4%ba%8b%e4%bb%b6nsevent/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注