2011年3月25日金曜日

GameKitで小数を送信する

GameKitでスコアやランキングデータを送信する時、Appleのドキュメントでは整数(int64)のデータを送信する方法しか載っていないので、タイムなどの小数(45.67秒等)を送信したい場合は一瞬戸惑います。

小数を送信する場合、NSNumberクラスを使用します。
NSNumberクラスはBoolやdouble,Int型などのprimitive型をオブジェクトにしたい場合に使用する便利なクラスです。


-(void)reportScores:(double)score forCategory:(NSString*)category
{
scoreReporter = [[[GKScore alloc] initWithCategory:category] autorelease];
NSNumber *numScore = [NSNumber numberWithDouble:round(score * 100)];
scoreReporter.value = numScore.intValue;
[scoreReporter reportScoreWithCompletionHandler:^(NSError* error) {
if (error != nil) {
// エラー処理
return;
}
}];
}


Appleのドキュメントに載っているスコア送信例を、double型を送信するように変更してあります。
上の例では、少数以下2桁を送信する例です。
引数のdouble型scoreを整数にしたいので100倍してNSNumber型のオブジェクトにします。
その後、そのint値(64ビット値)をGKScoreオブジェクトに設定しています。
勿論、iTunesでリーダーボードのスコア設定をDecimal小数2桁等、小数を扱うように設定しておく必要はあります。

後はAppleドキュメントに載っている様にランキング受信処理でGKScoreのformattedValueプロパティを参照すれば、iTunesで設定したフォーマットでNSString文字列が取得できます。

2011年3月2日水曜日

IOS4以降のアニメーション

IOS4以降のアニメーションは、[UIView animateWithDulation:delay:options:animations:completion:]を使います。IOS4以降でも従来のアニメーションは行えるようですが、非推奨となってしまったようです。
もっと簡易的に使えるメソッドもありますが、これだけ覚えておけば事足りるかもしれません。


[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
button.alpha = 0.0; // UIButtonインスタンスを徐々に透明に
}
completion:^(BOOL finished){
button.alpha = 1.0f; // buttonを現す
}];


第一引数はアニメーションにかける時間を指定します。上記例では0.5秒間でボタンを徐々に透明にしていきます。
delayはアニメーションを開始する時間を指定します。上記例では0なので即時アニメーション開始です。
optionsはアニメーションの動作のオプションです。上記例ではEaseOutで徐々に速度を落としてアニメーションをさせています。
animationsが実際のアニメーションの指定です。
completionはアニメーションが終了した時に呼び出される処理です。上記例では徐々に透明になったボタンの姿を再度現すようにしています。

2011年2月19日土曜日

プロビジョニングプロファイル

プロビジョニング等セキュリティ関連の情報をなんとなくまとめてみました。

ID/プロファイル内容
Bundle IDアプリケーション固有のID。アプリケーション1つに1個。(例:com.apple.iTunes)
App ID開発者がアプリケーションに「Push Notification」「in App Purchase」「Game Center」等へのアクセスを許可・禁止設定する為の挙動を管理するID。
キーチェインでのアクセス識別子となり、それぞれのアプリケーションの挙動を制御する。
アプリケーションの「挙動」について1つ付けられるIDの為、複数アプリケーションをまとめて指定できるワイルドカード指定が認められている。(例:com.apple.*)
ただし、GameCenter等のサービスを許可する時はワイルドカード指定は使えない。つまり各アプリケーションに1個のApp IDを付けなければならない。
プロビジョニングプロファイルデバイスにインストールされて、実行時に正規のルートからインストールされたか確認するための情報を保持するファイル。(App ID/デバイスID/開発者IDを保持)

2011年2月4日金曜日

メモリリークしない為に その1

NSMutableArrayやNSArrayをautorelease以外でメモリ確保した場合、配列を解放しても要素の解放処理が呼び出されるわけではありません。


ar1 = [[NSMutableArray alloc] initWithObjects:@"TEST1", @"TEST2", @"TEST3", nil];
ar2 = [[NSMutableArray alloc] initWithObjects:[SomeClass alloc]
, [SomeClass alloc]
, [SomeClass alloc], nil];

arrays = [[NSArray alloc] initWithObjects:ar1, ar2, nil];



[arrays release]; // ar1 / ar2 とSomeClassがメモリリーク


上記例では、「arrays」の解放しか行っていないので、「ar1」「ar2」「SomeClass」が全てメモリリークとなります。
「TEST1」「TEST2」「TEST3」の文字列はautoreleaseされるので解放の必要はありません。

要素を全て解放すればメモリリークしなくなります。

[ar1 release]; // ar1を解放
// ar2の要素を解放
    for (int i = 0; i < ar2.count; i++) {
    SomeClass* tmp = [ar2 objectAtIndex:i];
    [tmp release];
 }
[ar2 release]; // ar2を解放 [arrays release]; // arraysを解放

2011年1月18日火曜日

UINavigationBar

UINavigationBarをUINavigationControllerではなく、UIViewController等に独自に実装する場合、以下のようにします。


// ナビゲーションバーを生成
UINavigationBar* navBarTop = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
navBarTop.alpha = 0.7f;

// ナビゲーションアイテムを生成
UINavigationItem* title = [[UINavigationItem alloc] initWithTitle:@"Title"];

// 戻るボタンを生成
UIBarButtonItem* btnItemBack = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStyleBordered target:self action:@selector(clickBack:)];

// ナビゲーションアイテムの右側に戻るボタンを設置
title.rightBarButtonItem = btnItemBack;

// ナビゲーションバーにナビゲーションアイテムを設置
[navBarTop pushNavigationItem:title animated:YES];

// ビューにナビゲーションアイテムを設置
[self.view addSubview:navBarTop];


ナビゲーションアイテムを生成するときにタイトルを設定します。上記では「Title」という文字列を設定しました。これで画面上部に大きな文字で「Title」と表示されます。

また、ナビゲーションアイテムにiPhoneアプリでよく見られる、画面右上と左上に表示されるボタンなどを設置します。
上記では右上に「戻る」ボタンを追加していますが、leftBarButtonItemプロパティに設置すれば、左上にも表示できます。

UIBarButtonItemはカスタマイズ可能で、例えば

// segmentedControlはUISegementedControlのインスタンス
UINavigationItem* title = [[UINavigationItem alloc] initWithCustomView:segmentedControl];

等とすれば、ナビゲーションバーの内部にUISegmentedControlのインスタンスや、その他スライダー等、いろいろと設置することが可能なようです。

最後に「pushNavigationItem」でナビゲーションバーにアイテムを設置します。
「addSubview」を使いそうになりますので注意が必要です。

2011年1月9日日曜日

連続アニメーション

複数のアニメーションを連続して再生させるには、1つのアニメーションの終了を検知し、その後、続きのアニメーションを再生させます。

アニメーションの終了は「setAnimationDidStopSelector:finished:context:」でアニメーションの終了メソッドを登録しておきます。


[UIView beginAnimations:@"Animation1" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)];
// imgViewはUIImageViewのインスタンス
imgView.frame = CGRectMake(100, 200, imgView.frame.size.width, imgView.frame.size.height);
[UIView commitAnimations];



- (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void *)context
{
if ([animationID isEqualToString:@"Animation1"]) {
[UIView beginAnimations:@"Animation2" context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
imgView.frame = CGRectMake(100, 50, imgView.frame.size.width, imgView.frame.size.height);
[UIView commitAnimations];


上記例では、イメージ(imgView)を1秒間かけて(100, 200)に移動した後、続いて0.5秒間かけて(100, 50)に移動させます。

2010年12月10日金曜日

変更される可能性のあるクラスのメソッドを呼び出す

任意のクラスのメソッドを呼び出すには下記のように

[testClass testMethod];

等と呼び出せば良いですが、例えば上記の「testClass」がプログラム実行中に動的に変更されてしまう場合は以下のようにします。


objc_object* ptrClass = testClass;
SEL ptrMethod = @selector(testMethod:);
[ptrClass performSelector:ptrMethod withObject:パラメータ]; // 呼び出し


ptrClass = testClass2; // 動的にクラスを変更
ptrMethod = @selector(testMethod2:); // 動的にメソッドを変更
[ptrClass performSelector:ptrMethod withObject:パラメータ]; // 呼び出し


C++に慣れた方であればポインタ変数を任意のクラスにキャストしてメソッド呼び出しを行いそうですが、Objective-Cでは上記のようにします。

なお、withObjectでパラメータを複数渡したい場合は、複数オブジェクトを格納するNSArrayオブジェクトを渡すようにします。
また、afterDelay:で秒数を指定すれば、任意の秒数後にメソッド呼び出しが行われます。

NULLとnil

NULLとnilはどちらも値としては同じ0のようです。

しかし、NULLはポインタに対して空という意味であり、
nilはオブジェクトに対して空という意味を表すそうです。

CやC++言語でBOOL型に0かFALSEを使いましたが、それと似たような感じです。
現状ではNULLでもnilでもどちらでも良さそうですが、今後問題とならないように、きちんと使い分けをしておいた方が良いかもしれません。

ところで、NSMutableArrayにnilを追加することはできないようですが、NSNullという空の意味を表すオブジェクトが用意されており、これを使えば追加することができるそうです。

2010年12月8日水曜日

独自のUIViewを作る

独自のUIViewを作るには、描画処理を独自に作成する必要があります。

UIViewでは描画を行う時に「drawRect:(CGRect)rect」メソッドが呼び出されます。

このメソッドをオーバーライドして独自の描画処理を行わせます。

独自のUIViewを作成するのは、割と低レベル(OSに近くなると言う意味)なことですので、C言語の関数で描画を行う必要があります。また、フォントの扱い等、多少クセがあるようです。


-(void)drawRect:(CGRect)rect
{
// 描画範囲を取得
CGRect parentViewBounds = self.bounds;

// ビューのコンテクストを取得
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 塗りつぶし色設定
CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);

// 塗りつぶし色でビューを塗りつぶし
CGContextFillRect(ctx, CGRectMake(0, 0, parentViewBounds.size.width, parentViewBounds.size.height));

// 描画設定終了
CGContextClosePath(ctx);

// パスを描画
CGContextStrokePath(ctx);
}

UIViewのboundプロパティは、自身のUIViewの描画範囲をCGRect構造体で取得するプロパティです。

UIGraphicsGetCurrentContext()関数は、自身のUIViewインスタンスのコンテキストを取得する関数です。後の描画関数で引数として使用されます。

上記では、色の設定関数(CGContextSetFillColorWithColor)や塗りつぶし関数(CGContextFillRect)を呼び出していますが、実際の描画は最後に呼び出している描画関数(CGContextStrokePath)で行われます。色やパスの設定関数を呼び出した時点ではないことに少し注意が必要です。
例えば、色を何回変更してパスの設定を行ったとしても、実際の変更が行われるのはCGContextStrokePath等の描画関数を呼び出した時点ですので、途中の色設定は無視されて、最後の色設定のみが反映されることになります。

また、マウス操作などでリアルタイムに描画を変更したい場合などは、「setNeedsDisplayInRect」メソッドを呼び出せば、いつでもdrawRectメソッドがCallbackされます。

[self setNeedsDisplayInRect:self.frame];



描画を行う関数は大量にありますが、ほんの一部を抜き出すと、以下のようになります。
もっと知りたい方は、ドキュメントをCGContextで検索すればたくさん出てきます。

関数概要
CGContextAddLines線の描画設定
CGContextAddPathパスの描画設定
CGContextSetStrokeColorWithColor線の色設定
CGContextSetLineWidth線の太さ設定
CGContextSetFillColor塗りつぶし色設定
CGContextShowTextAtPointテキストの設定
CGContextSelectFontフォントの設定

上記表は描画関数のほんの一部です。上記表にはありませんが、イメージを描画したり、カーブ線を描画する関数もあるようです。

2010年12月1日水曜日

経過時間を取得する

経過時間を取得するには、以下のようにします。


NSDate* startTime = [[NSDate alloc] init];



NSTimeInterval time = [[NSDate date] timeIntervalSinceDate:startTime];

timeに経過秒数が格納されます。