cocoa - valor - tag servicio al cliente
¿Puedo recibir una devolución de llamada cada vez que se escribe una NSPasteboard? (5)
He leído la guía de programación de Appleboard , pero no responde una pregunta en particular que tengo.
Intento escribir una aplicación Cocoa (para OS X, no para iOS) que hará un seguimiento de todo lo que está escrito en el portapapeles general (por lo tanto, cada vez que una aplicación copie y pegue, pero no, digamos, drags-and-drops , que también hace uso de NSPasteboard). Podría (casi) lograr esto al sondear básicamente el portapapeles general en un subproceso de fondo constantemente, y verificar changeCount . Por supuesto, hacer esto me haría sentir muy sucio por dentro.
Mi pregunta es, ¿hay alguna manera de pedirle al servidor de Pasteboard que me notifique mediante algún tipo de devolución de llamada cada vez que se realiza un cambio en la mesa de trabajo general? No pude encontrar nada en la referencia de la clase NSPasteboard, pero espero que se oculte en otro lado.
Otra forma en que podría imaginar lograr esto es si hubiera una forma de cambiar la implementación general de un marco de trabajo con una subclase de NSPasteboard que pudiera definir para emitir una devolución de llamada. Tal vez algo como esto es posible?
Preferiría mucho si esto fuera posible con las API públicas y legales de App Store, pero si es necesario utilizar una API privada, también lo tomaré.
¡Gracias!
En base a la respuesta brindada por Joshua, se me ocurrió una implementación similar, pero de forma rápida, aquí está el enlace a su esencia: PasteboardWatcher.swift
Fragmento de código de la misma:
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 - [Swift - NSTimer does not invoke a private func as selector](http://.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
}
}
}
Nota: en el código compartido estoy tratando de identificar si el usuario ha copiado o no una url del archivo, el código proporcionado puede modificarse fácilmente para otros fines generales.
Hubo una vez una publicación en una lista de correo donde se describía la decisión contra una API de notificación. No puedo encontrarlo ahora mismo sin embargo. La conclusión fue que probablemente demasiadas aplicaciones se registrarían para esa API aunque realmente no lo necesitarían. Si luego copias algo, todo el sistema pasa por el nuevo contenido del portapapeles como loco, creando mucho trabajo para la computadora. Así que no creo que cambien ese comportamiento pronto. Toda la API de NSPasteboard está construida internamente en torno al uso de changeCount también. De modo que incluso su subclase personalizada de NSPasteboard debería seguir votando.
Si realmente desea comprobar si el cartón de embalaje ha cambiado, simplemente siga observando changeCount durante medio segundo. Comparar números enteros es realmente rápido, así que no hay ningún problema de rendimiento aquí.
Lamentablemente, el único método disponible es mediante sondeo (¡booo!). No hay notificaciones y no hay nada que observar para los contenidos modificados del portapapeles. Consulte el código de muestra ClipboardViewer de Apple para ver cómo se ocupan de inspeccionar el portapapeles. Agregue un temporizador (afortunadamente no excesivamente entusiasta) para seguir buscando diferencias y obtendrá una solución básica (si es tosca) que debería ser amigable para la tienda de aplicaciones.
Presente una solicitud de mejora en bugreporter.apple.com para solicitar notificaciones o alguna otra devolución de llamada. Lamentablemente, no lo ayudará hasta la próxima versión importante del sistema operativo como muy pronto, pero por ahora es una encuesta hasta que todos le pidamos que nos proporcione algo mejor.
No es necesario sondear. En general, la mesa de trabajo solo se modificará si la vista actual está inactiva o no tiene foco. Pasteboard tiene un contador que se incrementa cuando cambia el contenido. Cuando la ventana recupera el foco (windowDidBecomeKey), verifica si changeCount ha cambiado y luego procesa en consecuencia.
Esto no captura todos los cambios, pero permite que su aplicación responda si el Pasteboard es diferente cuando se activa.
En Swift ...
var pasteboardChangeCount = NSPasteboard.general().changeCount
func windowDidBecomeKey(_ notification: Notification)
{ Swift.print("windowDidBecomeKey")
if pasteboardChangeCount != NSPasteboard.general().changeCount
{ viewController.checkPasteboard()
pasteboardChangeCount = NSPasteboard.general().changeCount
}
}
Tengo una solución para casos más estrictos: detectar cuándo su contenido en NSPasteboard
fue reemplazado por otra cosa.
Si crea una clase que se ajuste a NSPasteboardWriting
y la pase a -writeObjects:
junto con el contenido real, NSPasteboard
retendrá este objeto hasta que se reemplace su contenido. Si no hay otras referencias fuertes a este objeto, se desasignará.
La desasignación de este objeto es el momento en que NSPasteboard
obtuvo contenido nuevo.