每当NSPasteboard被写入时,我可以收到一个callback吗?

我已经阅读了苹果公司的粘贴板编程指南 ,但是它并没有回答我的具体问题。

我正在尝试编写一个Cocoa应用程序(用于OS X,而不是iOS),它将跟踪写入到一般粘贴板的所有内容 (所以,只要有任何应用程序复制和粘贴,而不是拖动和拖放,也使用NSPasteboard)。 我可以(几乎)通过不断地在后台线程上查询普通的粘贴板并检查changeCount来完成这个任务。 当然,这样做会让我感到内心非常肮脏。

我的问题是,有没有办法要求粘贴板服务器通过某种callback通知我,每当改变普通的粘贴板? 我在NSPasteboard类参考中找不到任何东西,但是我希望它潜藏在其他地方。

另外一个我能想象得到的方法是,如果有一种方法可以用NSPasteboard的子类来replace一般的粘贴板实现,我可以定义自己发出callback。 也许这样的事情是可能的?

我非常希望如果这可能与公共,App Store合法的API,但如果使用私人API是必要的,我也会采取。

谢谢!

不幸的是,唯一可用的方法是轮询(booo!)。 没有通知,没有什么可以观察更改的纸板内容。 看看苹果的ClipboardViewer示例代码 ,看看他们如何处理检查剪贴板。 添加一个(希望不是过分热心)的计时器,以检查差异,你有一个基本的(如果笨拙的)解决scheme应该是应用程序商店友好。

在bugreporter.apple.com上提交增强请求,以请求通知或其他callback。 不幸的是,它不会帮助你,直到下一个主要的操作系统发布最早,但现在是投票,直到我们都要求他们给我们更好的东西。

在邮件列表上曾经有一个职位,其中描述了针对通知API的决定。 虽然我现在找不到它。 底线是,可能太多的应用程序将注册该API,即使他们真的不需要。 如果你复制一些东西,整个系统会像疯了一样通过新的剪贴板内容,为计算机创造大量的工作。 所以我不认为他们很快就会改变这种行为。 整个NSPasteboard API也是使用changeCount在内部构build的。 所以即使你的NSPasteboard的自定义子类仍然需要保持轮询。

如果你真的想检查纸板是否改变,只要继续观察changeCount半秒。 比较整数是非常快的,所以这里没有任何性能问题。

基于Joshua提供的答案,我提出了类似的实现,但很快,这里是它的要点链接: PasteboardWatcher.swift

相同的代码片段:

class PasteboardWatcher : NSObject { // assigning a pasteboard object private let pasteboard = NSPasteboard.generalPasteboard() // to keep track of count of objects currently copied // also helps in determining if a new object is copied private var changeCount : Int // used to perform polling to identify if url with desired kind is copied private var timer: NSTimer? // the delegate which will be notified when desired link is copied weak var delegate: PasteboardWatcherDelegate? // the kinds of files for which if url is copied the delegate is notified private let fileKinds : [String] /// initializer which should be used to initialize object of this class /// - Parameter fileKinds: an array containing the desired file kinds init(fileKinds: [String]) { // assigning current pasteboard changeCount so that it can be compared later to identify changes changeCount = pasteboard.changeCount // assigning passed desired file kinds to respective instance variable self.fileKinds = fileKinds super.init() } /// starts polling to identify if url with desired kind is copied /// - Note: uses an NSTimer for polling func startPolling () { // setup and start of timer timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: Selector("checkForChangesInPasteboard"), userInfo: nil, repeats: true) } /// method invoked continuously by timer /// - Note: To keep this method as private I referred this answer at stackoverflow - [Swift - NSTimer does not invoke a private func as selector](http://stackoverflow.com/a/30947182/217586) @objc private func checkForChangesInPasteboard() { // check if there is any new item copied // also check if kind of copied item is string if let copiedString = pasteboard.stringForType(NSPasteboardTypeString) where pasteboard.changeCount != changeCount { // obtain url from copied link if its path extension is one of the desired extensions if let fileUrl = NSURL(string: copiedString) where self.fileKinds.contains(fileUrl.pathExtension!){ // invoke appropriate method on delegate self.delegate?.newlyCopiedUrlObtained(copiedUrl: fileUrl) } // assign new change count to instance variable for later comparison changeCount = pasteboard.changeCount } } } 

注意:在共享代码中,我试图识别用户是否复制了一个文件的URL,所提供的代码可以很容易地修改为其他一般用途。

这是没有必要进行民意调查。 通常情况下,只有当前视图处于非活动状态或不具有焦点时才可以更改粘贴板。 粘贴板有一个计数器,当内容改变时递增。 当窗口重新获得焦点(windowDidBecomeKey)时,检查changeCount是否已经改变,然后相应地进行处理。

这不会捕获每个更改,但是如果在活动时Pasteboard不同,则让您的应用程序响应。

在Swift中…

 var pasteboardChangeCount = NSPasteboard.general().changeCount func windowDidBecomeKey(_ notification: Notification) { Swift.print("windowDidBecomeKey") if pasteboardChangeCount != NSPasteboard.general().changeCount { viewController.checkPasteboard() pasteboardChangeCount = NSPasteboard.general().changeCount } } 

对于更严格的情况,我有一个解决scheme:检测NSPasteboard中的内容何时被其他内容替代。

如果您创build一个符合NSPasteboardWriting的类,并将其传递到-writeObjects:以及实际内容,则NSPasteboard将保留此对象,直到其内容被replace。 如果没有其他强引用此对象,它将被释放。

这个对象的释放是新NSPasteboard获得新内容的时刻。