プロジェクトの作成
毎度のことながら前フリが長くてごめんなさい!
ようやく実作業に入ります。
Xcodeを起動して、Welcome画面から、
Create a new Xcode projectを選択。
左ペインのMac OS Xから
Applicationをクリック、右ペインで
Cocoa Applicationを選択して
Nextをクリックします。
次の画面では、アプリケーションの情報を設定します。
Product Nameには
ColorClick、
Company Identifierは
ドメインを逆順にしたもの(com.bornneet等)を、
Class Prefixは
CC、
App Store Categoryは
Graphics Designにしておきましょう。
チェックボックスは全て外して、
Nextをクリックします。
そうすると、保存ダイアログが出るので、
Create local git repository for this projectをチェックして、
Createをクリックします。
(gitリポジトリは任意ですが、作っておくとソースのバージョン管理ができ、特定のバージョンに戻したりといったことが簡単にできるようになります。使い方がわからなくても大きな変更の前後にメニューから
File→
Source Control→
Commitしておけば救われることがあるかもしれませんよ。)
これで無事プロジェクトが作成されました。
では早速、左上の
▶Runボタンを押してみましょう。
なんと何もコードを書いてないのに空のウィンドウを持ったアプリが実行されました。
素晴らしいですね!
半透明のウィンドウを表示する
カラーピッカーと言えば、半透明の黒いウィンドウが常に最前面にあるイメージです。
人間もアプリも見た目が全てではないですが、非常に大切なファクターではあります。
開発のモチベーションのためにも、まずは、それっぽい見た目にしてみましょう。
インターネットにはいろいろと古い情報もあって、結構面倒くさいんだなぁと萎えていたら、
(旧) Cocoaの日々: HUDを探して... に簡単な方法が載っていました。
xcatsanさんのブログはcocoaの情報満載で勉強になりまくるので是非チェックしてみて下さい。
それでは、手を動かしていきましょう。
Xcodeの左端のProject navigatorから、MainMenu.xibをクリックします。
するとGUIの編集画面(Xcode3までは“Interface Builder”として別アプリになっていたもの)に入りますので、
真ん中辺りのObjectsのWindow - ColorClickを選択しておきます。
そして、右下のObject Libraryから、HUD Window※2をドラッグ&ドロップで適当な位置に配置しましょう。
ObjectsにPanel - Windowが追加されます。
デフォルトだと、ウィンドウの座標やサイズがアプリケーションを終了するたびにリセットされてしまうので、右端のAttribute Inspectorから、WindowのAutosaveにColorWindow等と入力しましょう。
ここまで終わったらアプリケーションを実行してみてください。
サイズや位置が保存される半透明のウィンドウが表示されるはずです。
あと、タイトルがWindowになってしまっていて残念なので、Attribute InspectorでTitleを消しておきましょう。
※2 HUD WindowはNSPanel(NSWindowのサブクラス)なので、普通のウィンドウと挙動が違うのですが、今回はこれを使う前提で進めます。
ウィンドウの削除と再表示
現状だと、ColorClickというタイトルのウィンドウも表示されていますが、これはいらないので、
Interface Builder(以降IB)のObjectsからWindow - ColorClickを選択してdeleteキーで削除しましょう。
この状態で実行すれば半透明なウィンドウだけのおしゃれなアプリケーションになります。
ただ、このままだと重大な問題があります。
…そう、左上の×ボタンを押してウィンドウを閉じた後、Dockアイコンをクリックしてもウィンドウが再表示されないのです。
これは最悪ですね。
最も簡単な解決策は、Attribute Inspectorで半透明ウィンドウ(以降Panel)のCloseのチェックを外すことです。
これでウィンドウを閉じることができなくなります。
しかしこれはイマイチなので、少し頑張ってみたいと思います。
といっても少しの作業とコードで解決します。
まずは半透明ウィンドウをプログラムから操作できるようにOutletを設定します。
IBのObjectsで、controlキーを押しながらApp Delegateをクリックします。
すると以下のような画面が出てきます。
そうしたら、Outletsのwindowの右にある◯(マウスを持って行くと+が表示されます)をドラッグ&ドロップしてPanelと接続して下さい。
この作業では、CCAppDelegate.hに、
@property (assign) IBOutlet NSWindow *window;
として定義されている変数(プロパティ)にPanelを紐付けています。
これに加えて、CCAppDelegate.mに以下のコードが書かれていることで、プログラム内からself.window
として参照可能になります。(実は、先程削除したグレーのwindowが、元々このプロパティに接続されていました。)
@synthesize window = _window;
それではDockがクリックされた時にウィンドウを再表示するようにしましょう。
これは、CCAppDelegate.mに
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag {
// windowを前面に
[self.window makeKeyAndOrderFront:nil];
return NO;
}
というメソッドを定義すればOKです。
あとはウィンドウを閉じた時にインスタンスが解放されないように、IBのAttribute Inspectorから、PanelのRelease when closedのチェックを外しておきましょう。(これを忘れると解放されて既に存在しないPanelを再表示しようとして、アプリケーションが落ちます…。)
ここまでできたらアプリケーションを実行してみて下さい。
ウィンドウを閉じた後にDockのアイコンをクリックすると再表示されるはずです。
マウスの座標を取得する
いよいよメインの機能に関わる部分ですね。
まずは現在のマウスカーソルがある座標を取得してみます。
アプリの特性上この処理は、他のアプリケーションを操作中でも、マウスが動くたびに自動で行う必要があります。
これは、[NSEvent addGlobalMonitorForEventsMatchingMask:handler:
を使えば簡単に実現できます。
(参考:日暮れて道遠し: ユニバーサルアクセスの補助装置にアクセスできるようにする)
そして、肝心の座標ですが、CGEventGetLocation
で左下原点の座標が取得可能です。
それでは実装します。
本来はクラスを適切に分割すべきですが、今回は簡略化の為、全てCCAppDelegate.mに書いていきます。それではまず、アプリケーションの起動時に実行されるapplicationDidFinishLaunching:
メソッド内に以下を追記します。
// マウスが移動したら…
[NSEvent addGlobalMonitorForEventsMatchingMask:NSMouseMovedMask handler:^(NSEvent *event) {
// 自分のglobalMouseMoved:を呼ぶ
[self globalMouseMoved:event];
}];
このコードはマウスが移動するたびに、globalMouseMoved:
というメソッドを呼ぶという意味です。
では次に呼ばれるメソッドを書いていきます。
メソッドの中身を書く前に、#import "CCAppDelegate.h"と@implementation CCAppDelegateの間に以下を記述してメソッドを宣言します。(なくても動きますがエラーが出ますので書いておいた方が良いでしょう。また、CCAppDelegate.hでも宣言できますが、他のクラスから呼ばれないメソッドの場合は今回の書き方でOKです。)
@interface CCAppDelegate()
- (void)globalMouseMoved:(NSEvent *)event;
@end
つづいて、メソッドの中身を定義します。
- (void)globalMouseMoved:(NSEvent *)event {
// 左下原点のマウス座標を取得
CGEventRef eventRef = CGEventCreate(NULL);
CGPoint point = CGEventGetLocation(eventRef);
// CGEventRefの解放
CFRelease(eventRef);
// ログ表示(動作確認用)
NSLog(@"globalMouseMoved: x = %f, y = %f", point.x, point.y);
}
この状態でアプリケーションを実行すると、他のアプリケーションの操作中、マウスが移動するたび、XCode下部のDebug Areaに、
2012-02-06 22:22:33.537 ColorClick[18558:707]
globalMouseMoved: x = 1073.113281, y = 829.394531
2012-02-06 22:22:33.553 ColorClick[18558:707]
globalMouseMoved: x = 1074.160156, y = 830.437500
といったログが出力されるようになります。
画面キャプチャ
マウスの座標が取得できたので、次はその位置の色を取得すれば良さそうです。
しかし、いかに高機能なCocoaでもそんな便利メソッドはありません。(おそらく)
ただ、その周辺の情報が画像データとして手に入ればあとは解析すればなんとかなりそうな気はします。
というわけでマウスカーソル周辺の画面キャプチャを撮ってみましょう。
これはCGWindowListCreateImage
を用いて実装できます。
(参考:(旧) Cocoaの日々: 画面キャプチャその6 - 選択範囲をキャプチャ(完成))
まずは、キャプチャした画像を表示するパーツを作成します。
IBでImage WellをPanelに追加します。
そのままだと少しダサイので、Attribute InspectorでBorderをGrooveに変更します。
また、ScallingはProportionally Up or Down(Image Wellに合わせて拡大縮小される)に設定。
あとは、Size Inspectorで、Width・Heightを共に77にしておきましょう。(プログラミングの都合上11の倍数に)
次にプログラム上から操作できるようにOutletを設定します。
Xcode右上のEditorからAssistant Editor(蝶ネクタイ?アイコン)を選択します。
すると右側にコードエディタが表示されますのでCCAppDelegate.hを表示しておきましょう。
この状態で、先程追加したImage Wellから、controlキーを押しながら@endの上辺りまでドラッグドロップすると以下の画面が表示されますので、imageViewと入力してConnectをクリックして下さい。
これでプログラムから、self.imageView
として参照できます。
最後に、globalMouseMoved:
に以下のコードを追加します。(Editorは必要に応じてStandardに切り替えて下さい)
// キャプチャサイズ
int size = 11;
// 真ん中の座標
int center = floor(size / 2.0);
// マウス座標を中心にした矩形を作成
CGRect captureRect = CGRectMake(point.x - center, point.y - center, size, size);
// 画面キャプチャ
CGImageRef cgImageRef = CGWindowListCreateImage(captureRect,
kCGWindowListOptionOnScreenOnly, kCGNullWindowID,
kCGWindowImageBoundsIgnoreFraming);
// 表示するためにNSImageを作成
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:cgImageRef];
NSImage *image = [[[NSImage alloc] init] autorelease];
[image addRepresentation:bitmap];
// Image Wellに設定
self.imageView.image = image;
アプリケーションを実行してみましょう。良い感じですね!
(Panelが非アクティブな時しかうまく動作しないですが、妥協します)
画像からRGB値取得
必要な材料は揃ったので、画像を解析してRGBを取得します。
CGImageGetDataProvider
、CGDataProviderCopyData
、CFDataGetBytePtr
を使えば、先程キャプチャで得たCGImageRef
から、ビットマップデータの配列を取得することができます。
(参考:【コラム】実践! iPhoneアプリ開発 (4) カメラアプリの作り方 (4) - 写真にエフェクトをかける | エンタープライズ | マイナビニュース)
ではインタフェースから整えていきます。
MainMenu.xibを選択してIBを開きます。
まずは、Boxを追加して、Attribute InstectorでBox TypeをCustom、Border TypeをNone、Fill ColorをWhiteに設定します。サイズは適当な正方形にして右上に配置します。(このBoxの背景色を変更して取得したRGBを表示します。)
次にLabelは、Alignmentを右寄せ、Text ColorをWhite、Widthは130ぐらいにしてBoxの下に追加します。
(このLabelにはテキスト形式でRGBを表示します。)
あとは、どこの色を取得しているかわかりやすくするため、Image Wellの真ん中に7x7の白い枠だけのBoxを置いておくと良いでしょう。
さらにPanelのサイズも横250x縦400ぐらいに縮小しておきます。
このようになります。
次はプログラムから操作可能なように、右上のBoxをcolorBox、LabelをcolorLabelとしてOutlet設定します。
Assistant Editorを表示し、controlキーを押しながら、CCAppDelegate.hのコード内にドラッグ&ドロップしてConnectしましょう。
最後にコードですが、今回は画像の真ん中の座標のRGBだけがあれば良いので、以下のように実装しました。
キャプチャを11x11で取得しているので、座標x=5、y=5(キャプチャ時にcenter
という変数に入れておいたもの)のRGB値を取得しています。
なおビットマップの並びがBGRAになっているので注意して下さい。
(参考:CoreGraphicsでハマる - 定食屋おろポン)
// CGImageRefからビットマップデータを取得
CGDataProviderRef dataProvider = CGImageGetDataProvider(cgImageRef);
CFDataRef data = CGDataProviderCopyData(dataProvider);
UInt8* buffer = (UInt8*)CFDataGetBytePtr(data);
size_t bytesPerRow = CGImageGetBytesPerRow(cgImageRef);
// 中心のビットマップを取得
int x = center;
int y = center;
UInt8 *index = buffer + x * 4 + y * bytesPerRow;
// BGRA
UInt8 r = *(index + 2);
UInt8 g = *(index + 1);
UInt8 b = *(index + 0);
// Boxの背景色、Labelのテキストを設定
self.colorLabel.stringValue = [NSString stringWithFormat:@"#%x%x%x", r, g, b];
self.colorBox.fillColor = [NSColor colorWithDeviceRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0];
それでは実行してみます。
Goodですね。
クリックした色の履歴表示
ここまでで難しい部分は終わったので、あとはアプリが便利になるように機能を追加していきます。
まずは、クリックした場所の色を、表形式で履歴表示してみたいと思います。
カラーピッカー系のアプリって、その瞬間の色しかとれなくって不便なこととかあるので。
それではUIから。
IBでPanelにTable Viewを追加します。
Size InspectorでScroll View - Table…の幅を210、高さを170に設定し、続いて、Attribute InspectorでTable ViewのHeadersのチェックを外し、Grid StyleをHorizontal、Grid ColorをWhite、BackgroundをBlack、Focus RingをNoneにします。
TableColumnの1列目は、Identity InspectorでIdentifierを0にして、MinimumとWidthを20にし、中のText FieldのDraws Backgroundをチェック。
2列目は、Identifierを1にし、Text FieldのText ColorをWhiteにします。幅は180ぐらいに広げておきます。
なお、TableViewはプログラムから操作するため、tableViewとしてOutlet接続しておきましょう。
(誤ってScroll Viewを接続しないように注意!)
また、履歴が増え続けると困るので、削除ボタンも付けましょう。
Push ButtonをPanelに追加して、TitleをClearに変更すればOKです。
ここまでの作業でインタフェースはこのようになります。
それではクリック時の処理を実装していきます。
CCAppDelegate.hにitems_
という履歴を保持する配列と、現在の色を保持するred_
、green_
、blue_
という変数を追加します。
また、TableView関連の処理を実装することも宣言するため、, NSTableViewDataSource, NSTableViewDelegate
という記述もしておきます。
@interface CCAppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource, NSTableViewDelegate> {
NSMutableArray *items_;
UInt8 red_;
UInt8 green_;
UInt8 blue_;
}
配列はapplicationDidFinishLaunching:
の中で初期化します。
items_ = [[NSMutableArray array] retain];
アプリケーション終了時に解放する処理も忘れずに追加します。
- (void)dealloc
{
[items_ release];
[super dealloc];
}
それではマウスクリック時にitems_
に要素を追加してみましょう。
まず、今表示されている色の情報が欲しいので、globalMouseMoved:
の末尾にred_
、green_
、blue_
への代入処理を追加します。
red_ = r;
green_ = g;
blue_ = b;
クリックはマウス移動と同じく自分のアプリの外にある時に処理できるように、addGlobalMonitorForEventsMatchingMask:handler:
を使います。
[NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDownMask handler:^(NSEvent *event) {
[self globalMouseDown:event];
}];
globalMouseMoved:
と同じく、ファイル冒頭でメソッドの宣言も追加しておいて下さい。
次に、追加するデータ、つまりitems_
の1要素ですが、[RGBのテキスト値(#rgb), NSColorオブジェクト]
という配列にします。
つまり0個目の要素が白なら、
items[0][0]
がテキスト情報(#ffffff等)、
items[0][1](最後の要素)
が白を表すNSColorオブジェクトになります。
クリックのたびにこのデータを作成し、新しいものが上に来るように0番目に挿入します。
ただし、前回と同じ色なら追加しないほうが親切なので、items_
が1個以上あれば0番目と比較して同じでない場合のみ処理します。
これらを踏まえて実装したglobalMouseDown:
は以下のようになります。
- (void)globalMouseDown:(NSEvent *)event {
// 自分がアクティブか非表示なら何もしない
if ([self.window isKeyWindow] || ![self.window isVisible]) {
return;
}
// RGBの16進数文字列
NSString *hex = [NSString stringWithFormat:@"#%x%x%x", red_, green_, blue_];
// NSColorオブジェクト
NSColor *color = [NSColor colorWithDeviceRed:red_/255.0 green:green_/255.0 blue:blue_/255.0 alpha:1.0];
// 直前の色と同じじゃない場合のみ追加
if (items_.count == 0 || !([[[items_ objectAtIndex:0] lastObject] isEqualTo:color])) {
// [16進文字列, NSColor]という配列データを挿入する
NSArray *item = [NSArray arrayWithObjects:hex, color, nil];
[items_ insertObject:item atIndex:0];
}
// TableViewを更新する
[self.tableView reloadData];
}
そして、TableViewにこのitems_
の内容を表示するようにします。
2列目はテキストでRGBを表示し、1列目は背景にそのRGBで色をつけます。
これはそんなに難しくなく、以下の3メソッドを実装するだけです。
// TableView関連の処理
#pragma mark TableView
// TableViewの行数
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
// つまりitems_の要素数
return items_.count;
}
// TableViewの表示内容
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// 何列目か
switch ([tableColumn.identifier intValue]) {
// 2列目(Identifierが1)はitems_[raw][0]を表示
case 1:
return [[items_ objectAtIndex:row] objectAtIndex:0];
break;
// その他は(1列目も)表示内容無し
default:
return @"";
break;
}
}
// TableViewの表示直前に呼ばれる
- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// 1列目なら
if ([tableColumn.identifier isEqualToString:@"0"]) {
// 背景色をitems_[raw][last]に
[cell setBackgroundColor:[[items_ objectAtIndex:row] lastObject]];
}
}
上記メソッドが呼ばれるように、applicationDidFinishLaunching:
でTableView関連の各種処理と表示データの管理をCCAppDelegate(self)が担当するということを設定しておきます。
self.tableView.dataSource = self;
self.tableView.delegate = self;
最後にクリアボタンです。
Assistant Editorで、Buttonからcontrolキー+D&Dし、clearItemsとしてActionに接続します。
(OutletではなくActionなので注意)
これでボタンをクリックした時にclearItems:
メソッドが呼ばれます。
メソッド内の処理は単純で、items_
内を全削除し、TableViewを更新するだけです。
- (IBAction)clearItems:(id)sender {
[items_ removeAllObjects];
[self.tableView reloadData];
}
それでは実行してみましょう。
おぉ、アプリっぽくなってきましたね!
表示形式切り替え
大事な機能を忘れてました。
このままでは16進数しか使えませんので、他のアプリに負けてしまいます。
10進数でも表示できるようにしましょう。
まずは、IBでPanelの右下にPop Up Buttonを追加します。
次に、ObjectsからPop Up Buttonを選択し、Pop Up Button Cel→Menu - Other…とたどっていき、3つあるMenu Item…の最後を削除します。
そして、Attribute Inspectorで1つめのTitleを16進数を表す#rgbにしてState(チェック)をOnに、2つめのTitleは10進数を表すrgb()にしておきます。
こうなります。
それでは表示形式の切り替え処理を実装します。
Pop Upが変更された時に処理する為に、Actionを接続しましょう。
Assistant Editorでcontrolキーを押しながらCCAppDelegate.hを表示したエディタにD&Dし、changeColorFormatと入力してConnectをクリックします。
また、Pop Up Buttonはプログラムから選択状態を参照したいのでpopUpとしてOutletも接続しておきます。
changeColorFormat:
は以下のようになります。
// Pop Up Button変更時に呼ばれる
- (IBAction)changeColorFormat:(id)sender {
// 現在の状態を設定ファイル(NSUserDefaults)に保存
[[NSUserDefaults standardUserDefaults] setInteger:[sender indexOfSelectedItem] forKey:@"ColorFormatIndex"];
// TableViewを更新
[self.tableView reloadData];
}
選択状態はアプリを終了しても保持しておきたいので、NSUserDefaults
を利用して設定ファイルに保存しています。
また、アプリ起動時には保存した選択状態をPop Upに反映させる必要があるので、applicationDidFinishLaunching:
に以下のコードを追加します。
// 設定ファイルに選択状態が保存されていたらPop Upに反映する
NSInteger index = [[NSUserDefaults standardUserDefaults] integerForKey:@"ColorFormatIndex"];
if (index) {
[self.popUp selectItemAtIndex:index];
}
最後に選択状態に応じて表示を切り替えます。
まず、globalMouseMoved:
でLabelの文字列を設定している部分を変更します。
/* [変更前] */
//self.colorLabel.stringValue = [NSString stringWithFormat:@"#%x%x%x", r, g, b];
/* [変更後] */
if ([[NSUserDefaults standardUserDefaults] integerForKey:@"ColorFormatIndex"] == 1) {
self.colorLabel.stringValue = [NSString stringWithFormat:@"rgb(%d, %d, %d)", r, g, b];
} else {
self.colorLabel.stringValue = [NSString stringWithFormat:@"#%x%x%x", r, g, b];
}
これで、Labelの表示が切り替わります。
次にTableViewの為に、items_
に保持するデータを[16進, 10進, NSColorオブジェクト]
に変えます。
globalMouseDown:
を以下のように変更して下さい。
- (void)globalMouseDown:(NSEvent *)event {
// 自分がアクティブなら何もしない
if ([self.window isKeyWindow]) {
return;
}
// RGBの16進数文字列
NSString *hex = [NSString stringWithFormat:@"#%x%x%x", red_, green_, blue_];
/* [追記ここから] */
// RGBの10進数文字列
NSString *decimal = [NSString stringWithFormat:@"rgb(%d, %d, %d)", red_, green_, blue_];
/* [追記ここまで] */
// NSColorオブジェクト
NSColor *color = [NSColor colorWithDeviceRed:red_/255.0 green:green_/255.0 blue:blue_/255.0 alpha:1.0];
// 直前の色と同じじゃない場合のみ追加
if (items_.count == 0 || !([[[items_ objectAtIndex:0] lastObject] isEqualTo:color])) {
/* [変更ここから] */
// [16進文字列, 10進文字列, NSColor]という配列データを挿入する
NSArray *item = [NSArray arrayWithObjects:hex, decimal, color, nil];
/* [変更ここまで] */
[items_ insertObject:item atIndex:0];
}
// TableViewを更新する
[self.tableView reloadData];
}
そして、表示の際はNSUserDefault
の値を用いてitems_
からデータを取り出します。
つまり0番目が選択されていたらitems_[n][0]
なので16進数、
1番目が選択されていたらitems_[n][1]
なので10進数を表示できるというわけです。
コードは以下のようになります。
// TableViewの表示内容
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
// 何列目か
switch ([tableColumn.identifier intValue]) {
// 2列目(Identifierが1)はitems_[raw][0]を表示
case 1:
/* [変更ここから */
return [[items_ objectAtIndex:row] objectAtIndex:[[NSUserDefaults standardUserDefaults] integerForKey:@"ColorFormatIndex"]];
/* [変更ここまで */
break;
// その他は(1列目も)表示内容無し
default:
return @"";
break;
}
}
ちなみに背景表示の方はlastObject
を使っているのでコードを変更する必要はありません。(出来レースですけどね^^)
それではアプリケーションを実行してみます。
起動したらPop Upを切り替えて見てください。
無事、10進数表示ができました。
クリップボードにコピー
今の状態でも、セルをクリックした後少し時間をおいてクリックすれば編集モードになりコピーが可能ですが、さすがにこれは使いづらいです。
TablewViewダブルクリックした時に該当行のテキスト情報をコピーするようにしましょう。
applicationDidFinishLaunching:
に以下のコードを追加し、ダブルクリック時に、[self pasteToClipBoard]
を呼び出すように設定します。
(pasteToClipBoard
はglobalMouseMove:
、globalMouseDown:
同様に別途宣言も追加しておきます。)
self.tableView.target = self;
self.tableView.doubleAction = @selector(pasteToClipBoard);
コピーはNSPasteboard
というクラスを用いて行います。
pasteToClipBoard
は以下のようになります。
// TableViewダブルクリック時に呼ばれる
- (void)pasteToClipBoard {
// クリックされたrowのRGB値を文字列としてPasteboardにコピーする
NSPasteboard *pb = [NSPasteboard generalPasteboard];
[pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
[pb setString:[[items_ objectAtIndex:self.tableView.clickedRow] objectAtIndex:[[NSUserDefaults standardUserDefaults] integerForKey:@"ColorFormatIndex"]] forType:NSStringPboardType];
}
それでは実行してみてください。ダブルクリックするとコピーされることが確認できると思います。
簡単ですね。
(画面キャプチャは伝わらないので割愛します。)
広告
そろそろ大詰めです。
無料で配るアプリだってちょっとぐらいお小遣いになったほうが嬉しい。というわけで広告を貼りたいというのが自然な流れではあります。
…が、てっきりAdMobとかがやってると思ってたら、Mac用は全然無料のサービスがないんですね。
かろうじてBuySellAds.comがやってるらしいんですが、審査とか必要だし、そもそも日本に対応してるかよくわかりませんでした。
(参考:iphone - How to put ads in apps for Mac App Store? - Stack Overflow)
悔しいですが、今回はとりあえず適当なViewを表示しておくことにします。
バナーとリンク先はNode.js製のブログエンジンであるLooseLeaf.JSのものにしましょう。
もし本物の広告表示したい人がいたら連絡下さい(笑)
まずlogo.jpgから画像をダウンロードして、Project navigatorのSupporting Filesフォルダにドラッグ&ドロップします。
すると以下の画面が出てきますので、
Copy items into destination group's folder (if needed)にチェックを入れて、Create groups for any added foldersを選択し、Finishしましょう。
これでXcodeのプロジェクト内に画像がコピーされます。
この画像をPanel上に表示します。いろいろ方法はありますが、クリック時の処理が楽にできた方が良いので、ボタンを使うことにします。
IBでSquare Buttonを配置して下さい。Sizeは横を210pxぐらいまで大きくします。
そして、Attribute InspectorからImageをlogoに変更して下さい。
あと、WindowのHeightを小さくすることでせっかくの広告が消せないように、PanelのResizeをOFFにしておきましょう。
続いて、このボタンをクリックした時に、デフォルトブラウザで指定URLを開くようにしましょう。
クリック時の動作ですので、Actionを設定します。
もうお馴染みのAssistant Editor+controlキーD&Dで、CCAppDelegate.hに接続します。
アクション名は、openAdで良いでしょう。
中身は以下のようになります。
- (IBAction)openAd:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://looseleafjs.org/?utm_source=mac_app&utm_medium=banner&utm_campaign=color_click"];
// デフォルトブラウザで指定URLを開く
[[NSWorkspace sharedWorkspace] openURL:url];
}
クエリには万が一広告枠が売れることになった場合に備え、Analytics用の情報を入れておきます。
◯◯件アクセスされてるからこれぐらいで買ってよ、ということですね!
(参考:ツール: URL 生成ツール - アナリティクス ヘルプ)
では、実行してみましょう。
想定通り、ブラウザが開きました。
アイコン
デフォルトでアイコンがついていますが、これだとDockとかでソフトの判別がしにくい為、オリジナルのアイコンを設定しましょう。
アイコンはネットでフリーの素材やもしくはアイコンそのものを探して加工するのが早いです。
ただ今回は他の人の著作物を入れたくなかったし、Fireworksの使い方を絶賛勉強中なので、適当に作って見ました。
こんな感じです。
サイズ512×512で、形式はpngです。
この画像を、/Developer/Applications/Utilities/にあるIcon Composer.appを用いてアイコン化します。
Icon Composerを起動するとこのような画面になるので、先ほどの画像を左の512と書いてある欄にドラッグドロップします。
以下のようになるので、FileメニューのSave Asでicon.icnsとして保存します。
では設定しましょう。
Project navigator最上部のColorClickを選択し、TARGETSColorClickのSummaryタブを選択。
No image specifiedとなっているところに先ほど作成したicon.icnsをドラッグドロップしましょう。
(ColorClick-Info.plistを編集しても設定できます。)
実行してみましょう。
Dockのアイコンが変わりました。
メニューの削除とクレジット表示
仕上げに、必要のないメニュー項目を消してしまいましょう。
IBでメニュー項目を選択してdeleteキーを押せば削除可能です。
それでは、ColorClickのPreferencesServicesとSeparatorを2本、
→
Fileの、ClosePrint以外を(何も実装しなくても印刷機能がつくのがすごいですね!)、
→
EditFormatViewWindowHelpは特にあっても便利じゃなさそうなので、項目ごと消しちゃいましょう。
(長期的に拡張するアプリならいつか必要になるかもしれないので、消さないというのもありだと思います。あっても邪魔になるものでもないので)
↓
また、Supporting FilesのCredits.rtfを編集すれば、ColorClickメニューのAbout ColorClickに表示されるクレジット表示を変更できます。
今回は特に書くことないので空にしておきます。
ただ、会社名が、Copyright © 2012年 __MyCompanyName__. All rights reserved.になってしまっていて、あまりにも残念なので、ColorClick-info.plistを修正しておきます。
Copyright (human-readable)をCopyright © 2012 bornneet.com. All rights reserved.に変更します。
こうなります。
日本語化
そう言えば、せっかく思いついたダジャレアプリ名“カラクリ”がどこにも使われてないという悲しい状況です。これはまずいので国際化しましょう。といっても今回はアプリ名だけですが。
(参考:アプリケーションパッケージ名とアプリケーションメニューのローカライズ方法は異なる « Programmer’s High)
ColorClick-Info.plistでBundle nameを選択すると横に表示される+をクリックして、Bundle display nameを追加します。ValueはBundle nameのものをコピーして${PRODUCT_NAME}としておきます。
↓
こうすることで、この後設定する言語別のアプリ名が使われるようになります。
では早速設定しましょう。
InfoPlist.stringsのFile Inspectorを見ると、LocalizationがEnglishだけになっているので、+ボタンを押してJapanese (ja)を追加します。
→
この作業の後、Project navigatorで▶InfoPlist.stringsを開くと、
- InfoPlist.strings(English)
- InfoPlist.strings(Japanese)
の2ファイル分かれているので、それぞれ、
CFBundleName = "ColorClick";
CFBundleDisplayName = "ColorClick";
CFBundleName = "カラクリ";
CFBundleDisplayName = "カラクリ";
を追加しましょう。
これでOKです。実行してみると…
メニューとDockのアプリ名が日本語になりました。
パッケージング
いよいよ最後の仕上げです。
まさかユーザにXcodeでBuild & Runしてくれとお願いするわけにはいきませんから、.appファイルにパッケージングします。
これは超簡単です!
XcodeのProductメニューから、Archiveを選択します。
するとOrganizerが開くので、Shareをクリックします。
Contents:はApplicationを選択し、Nextをクリックすると、
保存ダイアログが出てくるのでデスクトップにでも保存しましょう。
これでデスクトップにappファイルができました。
それでは、実行してみましょう。
完成です!!
ダウンロード
完成版のアプリは以下からダウンロードできます。
color_click.zip
まだ荒削りな部分もありますが、入門編のサンプルアプリとしてはちょうどよい規模ではないかと思います。
足りない部分は是非ご自分の手でカスタマイズしてみて下さい。
ソースコード
ソースはあえて公開しません。
あんま意味なさそうだったので公開しました → 以前作ったMac用カラーピッカーアプリ「カラクリ」のソースコードを公開します - tobioka.net
自分で手を動かしながら、ハマったところをコピペで解決していくのはプログラミングのスキルアップにつながりますが、サンプルをそのまま動かしたり、forkするだけでは何の意味もないからです。
僕もこの記事の通り1回試して動作することを確認していますので、特にソースがないとダメなところはないと思います。
あと、配布しているアプリは広告付き版なのでうざいです。
というわけで広告無し版が欲しい人は自分でコーディングするしかない!という新たな取組みです。
プログラミングの勉強を始める時、短期的なゴールが見えづらい。
今回はこのアプリの広告無し版が2〜3時間ぐらいで手に入るはずです!
そして、自分で作れば自由に、例えばピンク色にしたり、背景にあの娘の写真を載せたりとか、いろいろできますしね。
名付けて全Macユーザ、Cocoaプログラマ計画です。是非挑戦してみてください。
最後に
いかがだったでしょうか?
まずはアウトプットを、ということで、いつも以上に浅い部分があったかもしれません。
日本語情報が少ない分野なので誰かの訳に立てば幸いです。
(しばらくMacアプリ系のエントリを投稿すると思うので興味があればまたチェックしてみて下さい)
突っ込み所がありましたら、以下のコメント欄から是非お願いします。(Mail、Facebook、Twetterでも大丈夫です)
記事の長さについては本当にすいません。僕も始めた時はここまで長くなるとは思ってなかったんです…。
単発記事じゃなく連載形式にしておくべきでした。
なお、ライセンスですが、本文部分はCreative Commons (BY-NC)にしときます。(なにげにCC初かも。)
ソースはそれだと使いにくいと思うのでMITで。
誰かiBooks Authorで電子書籍にしてくれないかなー。
1つお願いなんですが、上の趣旨があるので(意味があるかはわかりませんが)、広告を削っただけのカラクリをそのまま配布するのは避けてもらえるとありがたいです。もちろん、自分のアプリの中にコードを使ってもらうのは全然かまいません。
私には無理かも…とか考えている人、そんな暇があったら手を動かしてみましょう!
"Code wins arguments." 作ってみなきゃ何も始まりませんよ。
蛇足
丁寧な文体のほうが読む人はもちろん、書き手にとっても精神的な負担が少ない、ということがわかりました^^
それでは。
Happy Cocoa Programming :-)
PR