Могу ли я получить обратный вызов всякий раз, когда записывается NSPasteboard? - PullRequest
39 голосов
/ 17 февраля 2011

Я прочитал Руководство по программированию Apple Pasteboard , но оно не отвечает на конкретный вопрос, который у меня есть.

Я пытаюсь написать приложение Cocoa (для OS X,не iOS), которая будет отслеживать все, что записано на общий монтажный картон (то есть, когда любое приложение копирует и вставляет, но не, скажем, перетаскивание, которое также использует NSPasteboard),Я мог (почти) добиться этого, постоянно опрашивая общий монтажный стол в фоновом потоке и проверяя changeCount .Конечно, из-за этого я чувствую себя очень грязно изнутри.

Мой вопрос в том, есть ли способ попросить сервер Pasteboard уведомить меня через какой-то обратный вызов всякий раз, когда в изменение вносятся изменения?вообще картон?Я не смог найти ничего в справочнике по классу NSPasteboard, но я надеюсь, что он скрывается где-то еще.

Другой способ, которым я мог бы представить, что это можно сделать, - это если бы был способ поменять общую реализацию монтажной панели наподкласс NSPasteboard, который я мог бы определить для себя для обратного вызова.Может быть, что-то подобное возможно?

Я бы очень предпочел, если бы это было возможно с общедоступными API-интерфейсами App Store, но если необходимо использование частного API, я тоже это сделаю.

Спасибо!

Ответы [ 5 ]

40 голосов
/ 17 февраля 2011

К сожалению, единственный доступный метод - опрос (booo!).Нет уведомлений, и нечего наблюдать за измененным содержимым картона.Посмотрите пример кода Apple ClipboardViewer, чтобы узнать, как они справляются с проверкой буфера обмена.Добавьте (надеюсь, не переусердствуйте) таймер, чтобы постоянно проверять различия, и у вас есть базовое (если неуклюжее) решение, которое должно быть удобным для App-Store.

Подайте запрос на улучшение на bugreporter.apple.com для запроса уведомлений или другого обратного вызова.К сожалению, это не поможет вам до следующего релиза ОС, но пока это опрос, пока мы все не попросим их дать нам что-нибудь лучше.

12 голосов
/ 07 апреля 2012

Однажды в списке рассылки была запись, в которой было описано решение против API уведомления.Я не могу найти это прямо сейчас, хотяСуть в том, что, вероятно, слишком много приложений будут регистрироваться для этого API, даже если они на самом деле не понадобятся.Если вы потом что-то копируете, вся система проходит через новый контент буфера обмена, как сумасшедший, создавая много работы для компьютера.Поэтому я не думаю, что они изменят это поведение в ближайшее время.Весь API-интерфейс NSPasteboard также построен на основе changeCount.Таким образом, даже ваш пользовательский подкласс NSPasteboard все равно должен будет продолжать опрос.

Если вы действительно хотите проверить, изменился ли монтажный стол, просто продолжайте наблюдать за changeCount в течение очень половины секунды.Сравнение целых чисел действительно быстро, поэтому здесь нет проблем с производительностью.

10 голосов
/ 20 июня 2015

Основываясь на ответе, представленном Джошуа, я придумал аналогичную реализацию, но в быстром, вот ссылка на его суть: 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-адрес файла или нет, предоставленный код можно легко изменить для других общих целей.

1 голос
/ 31 июля 2017

Опрашивать не обязательно. Картон, как правило, будет изменен только в том случае, если текущий вид неактивен или не имеет фокуса. У картона есть счетчик, который увеличивается при изменении содержимого. Когда окно восстанавливает фокус (windowDidBecomeKey), проверьте, изменился ли changeCount, затем обработайте соответствующим образом.

Это не фиксирует каждое изменение, но позволяет вашему приложению реагировать, если Pasteboard отличается, когда он становится активным.

В Свифте ...

var pasteboardChangeCount = NSPasteboard.general().changeCount
func windowDidBecomeKey(_ notification: Notification)
{   Swift.print("windowDidBecomeKey")
    if  pasteboardChangeCount != NSPasteboard.general().changeCount
    {   viewController.checkPasteboard()
        pasteboardChangeCount  = NSPasteboard.general().changeCount
    }
}
0 голосов
/ 03 августа 2017

У меня есть решение для более строгого случая: определить, когда ваш контент в NSPasteboard был заменен чем-то другим.

Если вы создаете класс, соответствующий NSPasteboardWriting, и передаете его в -writeObjects:вместе с фактическим содержимым NSPasteboard будет сохранять этот объект до тех пор, пока его содержимое не будет заменено.Если нет других сильных ссылок на этот объект, он освобождается.

Удаление этого объекта - это момент, когда новый NSPasteboard получил новый контент.

...