DIYの最初のOS Xソフトウェア-クリップボードマネージャー

iOSプラットフォームのプログラミングに興味を持ち始めてから1年以上が経ちました。 最後に、OS Xプラットフォームを試してみる自由時間を見つけました.OS Xプラットフォームに長い間興味を持っているが、始めたくない場合は、この記事が役立ちます! 猫の下には、アプリケーションの作成プロセス(クリップボードマネージャー)の詳細な説明があります。 すべてのソースはgithub.com/k06a/Clipshareで見つけることができます



仕事のためにプロジェクトを準備します


ためらうことなく、Xcode 5で「Clipshare」と呼ばれるCocoa Applicationタイプのプロジェクトを作成しました。 新しく作成されたプロジェクトでは、次のファイルを確認できます。



ファイルABAppDelegate.hおよびABAppDelegate.mは、アプリケーションのデリゲートクラスに属し、それらにすべてのコードを記述します。 MainMenu.xibファイルは、アプリケーションGUIを描画およびカスタマイズします。

慎重に考えた後、アプリケーションをウィンドウなしにし、ステータスバー(時計の近く)でハングさせる必要があると判断しました。 最初に必要なことは、 MainMenu.xibファイルから標準のメニューバーとウィンドウオブジェクトを削除することです。これらは必要ありません。 次に、アプリケーションのセパレーターと終了ポイントの2つのポイントを持つメニューオブジェクトを作成します。 これは、オブジェクトをライブラリからキャンバスにドラッグアンドドロップするだけです。 Quitメニュー項目を機能させるには、Ctrlキーを押したまま、メニュー項目からApplicationオブジェクトまでマウスで青い糸を伸ばします。



マウスボタンを離すと、接続に使用できるセレクター(メソッド)のリストを含むウィンドウが表示されます。 提示された方法の中で、 -terminate適し-terminateます:



ここで、コードからメニューにアクセスできる必要があります。 OS Xのステータスバーに表示を設定するために、さらに多くのことを行います。 Xcodeウィンドウで2つのドキュメントがすぐに表示されたらアシスタントモードを開きましょう。最初はグラフィカルインターフェイスを終了し、2番目ではアプリケーションのソースコードABAppDelegate.hを選択します。 Ctrlキーを押したままの前回と同様に、メニューから青い糸を引き伸ばしますが、今度はソースコードまで引き伸ばします。 @interface@interfaceファイルの@interfaceコードセクションに@interfaceする必要があります(このファイルから不要なウィンドウプロパティを既に削除しました)。



押されたマウスボタンを離すとすぐに、作成されたアウトレット (GUIオブジェクトを参照するコード内のプロパティ)の設定を含むダイアログボックスがポップアップ表示されます。 プロパティの名前を指定するためだけに残ります(例: "menu"):



次に、アプリケーションのソースコードに切り替えます。 ファイルABAppDelegate.mにはメソッドが1つしかなく、そのメソッドは空です:

 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // ... } 

このメソッド内で、メニューを最初にOS Xステータスバーにバインドするコードを入力する必要があります。

 self.statusBar = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; self.statusBar.title = @"CS"; self.statusBar.menu = self.menu; self.statusBar.highlightMode = YES; 

このコードを機能させるには、 statusBarセクションでstatusBarプロパティも宣言する必要があります。

 @property (strong, nonatomic) NSStatusItem *statusBar; 

すでにアプリケーションをテストできるようにするには、 Clipshare-Info.plistファイルに値YESキーを1つ追加するだけで、ウィンドウなしでアプリケーションを動作させることができます。



アプリケーションを起動し、OS Xステータスバーで確認し、Cmd-Qを介して、または単一のメニュー項目をタップするだけで終了することもできます。



基本的なロジックを紹介します


クリップボードの内容をタイマーで0.5秒ごとにチェックし、その内容に応じて特定のアクションを実行することを提案します。 どうやら、クリップボードの変更について知る他の方法はありません。これは残念です( stackoverflow.com/a/5033480/440168 )。 OK、タイマーを設定します。 タイマーオブジェクトを作成し、どのメソッドとどのくらいの頻度で呼び出すべきかを伝え、タイマーをメッセージ処理ループに追加します。

 NSTimer * timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(timerFire:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 

これで、タイマーメッセージループがselfオブジェクトのセレクター(メソッド) -timerFire:を呼び出します。 このメソッドを実装することを忘れないでください:

 - (void)timerFire:(id)sender { NSPasteboard * pboard = [NSPasteboard generalPasteboard]; NSPasteboardItem * pboardItem = [[pboard pasteboardItems] lastObject]; NSString * text = [pboardItem stringForType:NSPasteboardTypeString]; // ... } 

メソッドの本体では、メインクリップボードとそれに含まれる最後のオブジェクトに目を向け、そこからテキストデータを抽出しようとします。 クリップボードの変更を以前の値の配列に保存すること、および対応する変更の日時の並列配列( ru.wikipedia.org/wiki/Parallel_array )を保存することをお勧めします。

 @property (strong, nonatomic) NSMutableArray * texts; @property (strong, nonatomic) NSMutableArray * times; 

プログラムの開始時に、両方のプロパティを空の負債で初期化することを忘れないでください。

 self.texts = [NSMutableArray array]; self.times = [NSMutableArray array]; 

ここで、次のロジックを実装します。以前の値を持つ配列のクリップボードで見つかったテキストを探し、そのような値がない場合は、配列の先頭に追加し、メニューの先頭に新しいアイテムを追加します。

 NSInteger index = [self.texts indexOfObject:text]; // ... NSMenuItem * menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:@selector(menuItemSelect:) keyEquivalent:@""]; [self.menu insertItem:menuItem atIndex:0]; [self.texts insertObject:text atIndex:0]; [self.times insertObject:[NSDate date] atIndex:0]; 

そのようなテキストが既に以前の値を持つ配列にある場合、メニュー内の対応する項目をチェックし、それ以上はチェックしません。 これを行うには、タイプBOOL新しいselectedIndexプロパティを作成します。

 if (index != NSNotFound) { self.selectedIndex = index; [self updateItemTitlesAndStates]; return; } 


updateItemTitlesAndStatesメソッドupdateItemTitlesAndStatesすべてのメニュー項目をupdateItemTitlesAndStatesし、それらの名前(名前はクリップボードにコピーしてからの経過時間を示します)を更新し、 selectedIndex項目の左側にチェックマークを付けます。

 - (void)updateItemTitlesAndStates { for (int i = 0; i < self.menu.itemArray.count-2; i++) { NSDate * time = self.times[i]; NSString * text = self.texts[i]; NSMenuItem * menuItem = self.menu.itemArray[i]; NSString * timeStr = nil; NSTimeInterval secs = MAX(0,[[NSDate date] timeIntervalSinceDate:time]); if (secs < 60) timeStr = [NSString stringWithFormat:@"%ds",(int)(secs)]; else if (secs < 60*60) timeStr = [NSString stringWithFormat:@"%dm",(int)(secs/60)]; else if (secs < 60*60*24) timeStr = [NSString stringWithFormat:@"%dh",(int)(secs/60/60)]; else if (secs < 60*60*24*7) timeStr = [NSString stringWithFormat:@"%dd",(int)(secs/60/60/24)]; else if (secs < 60*60*24*365.75) timeStr = [NSString stringWithFormat:@"%dw",(int)(secs/60/60/24/7)]; else if (secs < 60*60*24*365.75*3) timeStr = [NSString stringWithFormat:@"%dM",(int)(secs/60/60/24/30.5)]; else if (secs < 60*60*24*365.75*100) timeStr = [NSString stringWithFormat:@"%dy",(int)(secs/60/60/24/365.75)]; else timeStr = @".."; menuItem.title = [NSString stringWithFormat:@"(%@) \"%@%@\"", timeStr, [text substringToIndex:MIN(MaxVisibleChars,text.length)], (text.length <= MaxVisibleChars) ? @"" : @"..."]; menuItem.state = (i == self.selectedIndex) ? NSOnState : NSOffState; menuItem.keyEquivalent = [@(i+1) description]; } } 

新しいアイテムを追加する場合、古いアイテムが多すぎる場合は削除する必要があります。

 while (self.menu.itemArray.count >= MaxVisibleItems+2) { [self.menu removeItemAtIndex:self.menu.itemArray.count-3]; [self.texts removeLastObject]; [self.times removeLastObject]; } 

メニュー項目のクリックを処理するために残ります:

 - (void)menuItemSelect:(id)sender { NSInteger index = [self.menu.itemArray indexOfObject:sender]; NSPasteboard * pboard = [NSPasteboard generalPasteboard]; [pboard clearContents]; NSPasteboardItem * pboardItem = [[NSPasteboardItem alloc] init]; [pboardItem setString:self.texts[index] forType:NSPasteboardTypeString]; [pboard writeObjects:@[pboardItem]]; } 


これで、アプリケーションは希望どおりに動作します すでにそこにあったテキストが最近クリップボードに到達すると、チェックマークが目的の項目にジャンプします。 このアイテムをクリックした場合も同じことが起こります。




このアプリケーションはテキストデータ専用に設計されており、クリップボードに保存されている写真、ファイル、その他のものでは正常に動作しませんが、私は数日間自分で使用しています。 これは、私が数時間で書いた最初のアプリケーションです。実際に使用しています。 すべての写真を読んで見た人のおかげで、あなたがそれを楽しんだことを願っています!

コード内のマジックナンバーと並列配列については、キックしないでください! 簡単で高速だったので、書きました。 風水のすべてを愛している人-私はあなたのプールのリクエストを待っています!

アプリケーションのソースは次の場所にあります: github.com/k06a/Clipshare
バイナリはリリースからダウンロードできます: github.com/k06a/Clipshare/releases

PS賛否両論を軽視しない場合は、次の記事でiOS用のアプリケーションを作成し、デバイス間でクリップボードを同期します!

PPSテキスト内で壮大なタイプミスを見つけた人は誰でも、カルマでプラスになります!

Source: https://habr.com/ru/post/J197348/


All Articles