Подтвердить что ты не робот

Могу ли я получить обратный вызов всякий раз, когда записывается NSPasteboard?

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

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

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

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

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

Спасибо!

4b9b3361

Ответ 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 получил новое содержимое.