hishidaの開発blog

EBシリーズ(EBPocket,EBWin,EBMac,EBStudio),KWIC Finder,xdoc2txt,読書尚友の開発者ブログ

EBPocket for iOS クリップボード変更検知によるクリップボード検索

クリップボード検索というと、たいていの方は、「他のアプリと辞書アプリを同時に立ち上げて、他アプリでテキストをコピーすれば即座に辞書引きができる」という利用方法を期待するのではないだろうか。
だが、EBPocketのクリップボード検索は、アプリケーションがアクティブになるタイミングでしか検索できなかった。
このたび、iPad のSplit Viewによるマルチタスク環境において、EBPocket for iOSクリップボード変更検知によるクリップボード検索が、やっとできるようになった。


iOSのペーストボードには、次のように変更を検知する機能が元々ある。

[[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(pasteboardChanged:)
     name:UIPasteboardChangedNotification
     object:[UIPasteboard generalPasteboard]];

ところが、Split Viewで別のアプリがペーストボードを変更しても、このNotificationは発火しない。このため、てっきり変更検知はできないと思い込んでいた。
ところが最近、「物書堂の辞書アプリではできている」という報告をいただいた。物書堂さんができているなら、EBPocketでもできないはずはない。

色々方法を探ったところ、結局、次の記事の方法で解決した。単純にタイマーで定期的にペーストボードの内容をチェックするだけだ。

stackoverflow.com

1つ注意点は、iOS14以来、他のアプリが書き込んだペーストボードの情報を読み込むと、"EBPocketに○○からペースト"みたいなアラートが表示されることである。
このため、実際に変更された場合だけ読み込むようにしないと、タイマーの間隔毎にアラートが表示されることになってしまう。そこで、ペーストボードのchangeCountを見て、数字が変わったときだけデータを取得する。

実は上記の記事のコードには、一部コンパイルできない箇所があるので、実際に使ったコードを以下に示す。
クリップボード監視を開始するときにstartCheckingPasteboardを呼び、終了時にstopCheckingPasteboardを呼ぶ。

@property (strong, nonatomic) NSTimer   *pasteboardCheckTimer;
@property (assign, nonatomic) NSUInteger pasteboardchangeCount;


- (void) startCheckingPasteboard{

    _pasteboardchangeCount = [[UIPasteboard generalPasteboard] changeCount];


    //Start monitoring the paste board
    _pasteboardCheckTimer = [NSTimer scheduledTimerWithTimeInterval:1   //  1s間隔
                                                     target:self
                                                   selector:@selector(monitorBoard:)
                                                   userInfo:nil
                                                    repeats:YES];
}

- (void) stopCheckingPasteboard{

    [_pasteboardCheckTimer invalidate];
    _pasteboardCheckTimer = nil;
}

- (void) monitorBoard:(NSTimer*)timer{

    UIPasteboard *_pasteboard = [UIPasteboard generalPasteboard];
    NSUInteger changeCount = [_pasteboard changeCount];
    if (changeCount != _pasteboardchangeCount) { // means pasteboard was changed
        _pasteboardchangeCount = changeCount;

       if ([_pasteboard hasStrings]){
            NSString *newContent = _pasteboard.string;

            [self tryToDoSomethingWithTextContent:newContent];
        }
    }
}

- (void)tryToDoSomethingWithTextContent:(NSString*)string{
    //    取得したペーストボードの内容で処理を行う。

}