Cocoa 每当写入NSPasteboard时,我是否可以收到回调?
我读过苹果的,但它没有回答我的一个问题 我正在尝试编写一个Cocoa应用程序(针对OSX,而不是iOS),它将跟踪写入的所有内容(因此,无论何时,只要有任何应用程序复制和粘贴,而不是拖放,这也使用了NSPasteboard)。我可以(几乎)通过不断地在后台线程上轮询通用粘贴板并进行检查来实现这一点。当然,这样做会让我内心感到非常肮脏 我的问题是,有没有办法让粘贴板服务器在对常规粘贴板进行更改时通过某种回调通知我?我在NSPasteboard类引用中找不到任何内容,但我希望它隐藏在其他地方 我可以想象实现这一点的另一种方法是,如果有一种方法可以用NSPasteboard的子类替换常规的pasteboard实现,我可以定义自己来发出回调。也许这样的事情是可能的 我非常希望在公共应用商店的合法API中实现这一点,但如果有必要使用私有API,我也会这么做Cocoa 每当写入NSPasteboard时,我是否可以收到回调?,cocoa,macos,nspasteboard,Cocoa,Macos,Nspasteboard,我读过苹果的,但它没有回答我的一个问题 我正在尝试编写一个Cocoa应用程序(针对OSX,而不是iOS),它将跟踪写入的所有内容(因此,无论何时,只要有任何应用程序复制和粘贴,而不是拖放,这也使用了NSPasteboard)。我可以(几乎)通过不断地在后台线程上轮询通用粘贴板并进行检查来实现这一点。当然,这样做会让我内心感到非常肮脏 我的问题是,有没有办法让粘贴板服务器在对常规粘贴板进行更改时通过某种回调通知我?我在NSPasteboard类引用中找不到任何内容,但我希望它隐藏在其他地方 我可以
谢谢 不幸的是,唯一可用的方法是通过轮询(booo!)。对于更改的粘贴板内容,没有通知,也无需观察。看看苹果公司,看看他们是如何处理检查剪贴板的。添加一个(希望不是过于热心的)计时器来不断检查差异,这样你就有了一个基本的(如果笨重的话)解决方案,应该对应用商店友好
在提交增强请求以请求通知或其他回调。不幸的是,在最早的下一个主要操作系统发布之前,它不会对您有所帮助,但现在它只是轮询,直到我们都要求他们给我们提供更好的东西。曾经在邮件列表上有一篇文章,其中描述了反对通知api的决定。不过我现在找不到。底线是,可能有太多的应用程序会注册该api,即使它们真的不需要注册。如果你再复制一些东西,整个系统会疯狂地浏览新的剪贴板内容,为计算机创造大量的工作。所以我认为他们不会很快改变这种行为。整个NSPasteboard API也是使用changeCount在内部构建的。因此,即使是NSPasteboard的自定义子类也必须保持轮询
如果您真的想检查粘贴板是否发生了更改,只需持续观察更改计数半秒。比较整数的速度非常快,因此这里没有性能问题。根据Joshua提供的答案,我提出了类似的实现,但在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是否已更改,然后进行相应的处理 这不会捕获每一个更改,但是如果粘贴板在激活时不同,应用程序可以做出响应 在斯威夫特
var pasteboardChangeCount = NSPasteboard.general().changeCount
func windowDidBecomeKey(_ notification: Notification)
{ Swift.print("windowDidBecomeKey")
if pasteboardChangeCount != NSPasteboard.general().changeCount
{ viewController.checkPasteboard()
pasteboardChangeCount = NSPasteboard.general().changeCount
}
}
对于更严格的情况,我有一个解决方案:检测
NSPasteboard
中的内容何时被其他内容替换
如果创建符合NSPasteboard Writing
的类并将其与实际内容一起传递给-WriteObject:
,NSPasteboard
将保留此对象,直到其内容被替换。如果没有对该对象的其他强引用,则会取消分配该对象
解除分配此对象是new
NSPasteboard
获得新内容的时刻。对于那些需要Swift 5中一些非常简化的版本的人,它只起作用(基于@Devarshi代码):
如何使用的用法如下:
WatchPasteboard {
print("copy detected : \($0)")
}
它将像下面那样打印出来
watched : pasteboard1
watched : pasteboard2
我很害怕。谢谢!:)从2.5年前开始有什么变化吗?从3年前开始有什么变化吗?到目前为止,我没有看到任何变化,但考虑到最新版本(约塞米蒂10.10版和iOS 8版),粘贴板可能会很好地保持原来的简单,因为苹果正在兜售一种更现代、更具内容意识的机制,用于在应用程序(和设备)之间传递信息。突然之间,对于那些对管理临时存储空间感兴趣的应用程序来说,粘贴板似乎不再是一个目标…;-)@令人遗憾的是,它看起来是这样的。没有允许回调的新API。这不是答案,但如果您一般都在监视粘贴板,则需要注意:有一个非正式的协议在粘贴板上标记瞬态和应用程序生成的数据:假设甚至需要“瞬时”。我想知道是否真的有必要记录每一个变化(因此需要像孩子一样反复询问,每一秒都要问“我们到了吗?”)当用户调用您的服务时(否则,您就是一个浪费资源、爱管闲事的应用程序)是一个好时机。若你们必须捕捉背景更新(可能是日志),也许每隔几秒钟就可以了。如果用户在几秒钟内复制了两次,第一次可能是他们后来纠正的错误……老实说,让NSPasteboard检查changeCount并比较两个整数有什么浪费?我同意这种行为在90年代早期和之前是浪费的,但现在不行。现代笔记本电脑的功耗对你来说并不重要?现在,让你的应用程序进入睡眠状态是至关重要的,除了一个目的是记录粘贴板上的每一件事情的应用程序之外
watched : pasteboard1
watched : pasteboard2