Могу ли я получить обратный вызов всякий раз, когда записывается NSPasteboard?
Я прочитал Apple Руководство по программированию карманов, но он не отвечает на конкретный вопрос, который у меня есть.
Я пытаюсь написать приложение Cocoa (для OS X, а не iOS), которое будет отслеживать все, что записано в общий картон (так, всякий раз, когда какое-либо приложение копирует и вставляет, но не скажет, перетаскивает и капли, что также использует NSPasteboard). Я мог (почти) выполнить это, в основном, опросив общий картон на фоновом потоке постоянно и проверив changeCount. Конечно, это сделало бы меня очень грязным внутри.
Мой вопрос: есть ли способ попросить сервер Pasteboard уведомить меня о каком-то обратном вызове в любое время, когда будет внесено изменение в общий картон? Я не мог найти ничего в ссылке класса NSPasteboard, но я надеюсь, что он скрывается где-то еще.
Еще один способ, которым я мог бы предположить, что это можно сделать, - это возможность заменить общую реализацию картонной подклассы подклассом NSPasteboard, чтобы я мог определить, что нужно сделать обратный вызов. Может быть, что-то подобное возможно?
Я бы очень хотел, чтобы это было возможно с помощью общедоступных API-интерфейсов App Store, но если вам нужен частный API, я тоже это сделаю.
Спасибо!
Ответы
Ответ 1
К сожалению, единственный доступный метод - опрос (booo!). Нет никаких уведомлений, и там нечего наблюдать за измененным содержимым картона. Ознакомьтесь с Apple образцом кода ClipboardViewer, чтобы узнать, как они справляются с проверкой буфера обмена. Добавьте (надеюсь, не чрезмерно усердный) таймер, чтобы продолжать проверять различия, и у вас есть базовое (если это неудобное) решение, которое должно быть App-Store-Friendly.
Введите запрос расширения на bugreporter.apple.com для запроса уведомлений или другого обратного вызова. К сожалению, это не поможет вам до следующей крупной версии ОС, но на данный момент это опрос, пока мы все не попросим их дать нам что-то лучшее.
Ответ 2
Когда-то была почта в списке рассылки, где было описано решение об уведомлении api. Однако я не могу найти его прямо сейчас. Суть в том, что, вероятно, слишком много приложений будут регистрироваться для этого api, даже если им действительно не нужно. Если вы затем копируете что-то, вся система просматривает новый контент буфера обмена, как сумасшедший, создавая много работы для компьютера. Поэтому я не думаю, что они изменят это поведение в ближайшее время. Весь API-интерфейс NSPasteboard встроен также с помощью changeCount. Поэтому даже ваш пользовательский подкласс NSPasteboard все равно должен будет продолжать опрос.
Если вы действительно хотите проверить, изменился ли картон, просто продолжайте наблюдать за changeCount в течение половины секунды. Сравнение целых чисел происходит очень быстро, поэтому здесь нет проблем с производительностью.
Ответ 3
На основании ответа, предоставленного Джошуа, я придумал аналогичную реализацию, но быстро, вот ссылка на его суть: 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 или нет, предоставленный код может быть легко изменен для других общих целей.
Ответ 4
Не нужно опроса. Pasteboard обычно может быть изменен только в том случае, если текущее представление неактивно или не имеет фокуса. У Pasteboard есть счетчик, который увеличивается при изменении содержимого. Когда окно восстанавливает фокус (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
}
}
Ответ 5
У меня есть решение для более строгого случая: обнаружение, когда ваш контент в NSPasteboard
был заменен чем-то другим.
Если вы создадите класс, соответствующий NSPasteboardWriting
, и передайте его -writeObjects:
вместе с фактическим содержимым, NSPasteboard
сохранит этот объект до тех пор, пока его содержимое не будет заменено. Если нет других сильных ссылок на этот объект, он освобождается.
Освобождение этого объекта - это момент, когда новый NSPasteboard
получил новое содержимое.