iOS3.2からUIBezierPathが利用できるようになりました。
2次元グラフィックを扱う人にとっては朗報ですね。
ところが、実際にUIBezierPathを使ってプログラミングしていたら驚くべきことが分りました。
UIBezierPathを使って多めの線分(3000本)を一気に描画してみました。
(1)普通のケース
- (void)drawRect:(CGRect)rect {
// 諸々描画準備
UIBezierPath *path = [[UIBezierPath alloc]init];
//path.lineWidth = 2; // 2ptの太線にする(今はコメントアウトしておく)
for (int i = 0; i < 3000; i++) {
double sx = (rand() % 10) / 10.0 * w;
double sy = (rand() % 10) / 10.0 * h;
double ex = (rand() % 10) / 10.0 * w;
double ey = (rand() % 10) / 10.0 * h;
[path moveToPoint:CGPointMake(sx, sy)];
[path addLineToPoint:CGPointMake(ex, ey)];
}
CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);
NSTimeInterval started = [[NSDate date] timeIntervalSinceReferenceDate];
[path stroke];
NSLog(@"done:%f", ([[NSDate date] timeIntervalSinceReferenceDate] - started) * 1000);
}
実行結果は
>done:265.557945
300ミリ秒弱、ちょっと遅い感じもしますが、、、こんなものでしょうか。
(2)太線にしたケース
上のコードの以下の部分を有効にしてみます。
path.lineWidth = 2; // 2ptの太線にする
実行結果は
>done:178170.634985
え?180秒?3分掛ってる?何かの間違いか?もしや、、これはJavaでも散々問題が発生したアレか?
試しにアンチエイリアスをオフる。
実行結果は
>done:809.293985
うぅ、やっぱり。
大量のグラフィックを描画する場合には、アンチイリアスが必要なものとそうでないものを分けて描画しましょう。
方法はこんな感じです。
// アンチエイリアスをOFFにする。 CGContextSetAllowsAntialiasing(context, false); CGContextSetShouldAntialias(context, false);
せっかくの美しい画面がジャギジャギですが、背に腹はかえられません。
何かもっと良い方法があるに違いないのですが、今のところ分りません。
| |trackbacks(3) |18:45 2010/11/18 |
Appleは、iPhone4のアンテナ感度がどうしたこうしたという問題を解決するために、何種類かのiPhone専用ケースを無償配布しました。その中で恐らく一番人気だったのが「バンパー」。確かApple純正ですよね?隙間なくキレイに収まって素敵です。
んが、しかし!!同じくApple純正のVGAアダプターが刺さらないじゃないかー!!
一見するとちゃんと刺さってるように見えるんですが、ケースの厚みの分だけ浮いてます。VGAアダプターの根っこがちょこっと太いんです。
これではVGA出力できませんよね。あまりにシドイ…小一時間悩みました。VGA出力できないみなさん、ひょっとしたら原因は「バンパー」かもしれませんよ。
「タダより高いものはない」
| |trackbacks(0) |21:24 2010/11/08 |
サッカーワールドカップが遠い昔のことのように思えます。
当時、ワールドカップ向けのアプリがたくさんリリースされていました。
有名どころとしてはバーチャルブブゼラでしょうか。無料版、有料版が用意されていて、有料版はただ音が大きい(笑)というお遊びアプリでした。
私もこれに負けじと、イエロー・レッドカードなるアプリを申請しました。
アプリを起動すると下のような画面が表示されて、レッドカードかイエローカードを選択するとホイッスルの音がして画面が真っ赤、または真っ黄色になる、という実にナイスなアプリでした。
スポーツバーでみんながこのアプリで遊んでいるところが目に浮かびました…
ところが、アップルのレビューでRejectされていまいました。Rejectの理由は、噂の「機能無し」でした!
おおいに笑わせていただきました。が、もう少し洒落を分ってくれてもいいですよね!

| |trackbacks(1) |15:47 2010/11/05 |
OpenGLでぶんぶんループを回っているようなアプリは、かなりの勢いで電池を消費するようです。
プレイヤーが考えている時間が長いパズルゲームを作ってしまったので、プレイしている間に消費電力を抑える工夫が必要でした。
そこで一定の間操作が無かったらOpenGLのループを回さない、というごく単純な戦略をとりました。
// アニメーション開始
- (void)startAnimation {
// 動作中のタイマーがあったら停止
if (animationTimer != nil && [animationTimer isValid]) {
[animationTimer invalidate];
animationTimer = nil;
}
// タイマー開始時間をメモ
startTime = [NSDate timeIntervalSinceReferenceData];
// 描画タイマー開始(30fpsをメドで再描画)
animationTimer =
[NSTimer scheduledTimerWithTimeInterval:1.0/30
target:self selector:@selector(draw) userInfo:nil repeats:TRUE];
}
// アニメーション停止
- (void)stopAnimation {
if (animationTimer != nil && [animationTimer isValid]) {
[animationTimer invalidate];
}
animationTimer = nil;
}
// ドラッグイベントハンドラ
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
// 操作時間更新
startTime = [NSDate timeIntervalSinceReferenceDate];
// ドラッグの処理
}
// 描画メソド
- (void)draw{
// オブジェクトの描画
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval interval = now - startTime;
// 何もしていない時間が5秒あったらループ停止
if (interval > 5) {
[self stopAnimation];
}
}
こんな感じでアニメーションを停止するようにしました。
これで触っていないときに電池の減りを抑えることができました。
| |trackbacks(2) |15:08 |
今回はiPhone/iPadアプリ内でXMLをパースする方法について書いてみます。
私の場合、iPhone/iPadでXML形式のデータを扱うのは主に以下のケースです。
まずはDOMでいくか、SAXでいくか選択します。小さいデータならDOMが扱いやすいので、普通にDOM形式で読みこんでしまいます。
問題になるのは、ちょっと大きめのデータを扱う場合で、DOM形式で読みこんでから自分のオブジェクトに変換したのでは一時的に多くのメモリを必要としてしまいます。読込しながら自分のオブジェクトに変換していきたいところですから、定石どおりSAXを利用することにします。
SAXを利用する場合はNSXMLParserを使うか、libxmlを使うか、の二つの選択肢が標準的に用意されています。NSXMLParserは使いやすく、libxmlは高速である、との情報がありました。もちろん高速な方(libxml)を選択しますよ!
SAXはXMLの構文解析をしながら、適切なメソドを呼び出してくれますので、我々はイベントハンドラの登録と、イベントハンドラの実装を行います。
// タグを読み込んだら呼び出されます。 static void startElementHandler(void* ctx, const xmlChar* localname, const xmlChar* prefix, const xmlChar* URI, int nb_namespaces, const xmlChar** namespaces, int nb_attributes, int nb_defaulted, const xmlChar**attributes); // タグが閉じられたら呼び出されます。 static void endElementHandler(void* ctx, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri); // 文字列を読み込んだら呼び出されます。 static void charactersFoundHandler(void* ctx, const xmlChar* ch, int len);
それぞれのハンドラではタグ名等に応じて独自形式のオブジェクトを作成し、設定していけば良いのですが、startElementHandlerでElementから属性値を取得するのがとても面倒です。速い方を選択した代償だと思って頑張りましょう!以下にその方法を抜粋して記述します。
if (nb_attributes > 0) {
NSMutableDictionary *attributes = [[[NSMutableDictionary alloc]init]autorelease];
for (int i = 0; i < nb_attributes; i++) {
NSString *key = [NSString stringWithCString:(const char*)charAttributes[0] encoding:NSUTF8StringEncoding];
int length = charAttributes[4] - charAttributes[3];
NSString *val = [[NSString alloc]initWithBytes:charAttributes[3] length:length encoding:NSUTF8StringEncoding];
[attributes setObject:val forKey:key];
charAttributes += 5;
}
// TODO: ここでオブジェクトに属性(attributes)をセットして下さい。
}
MAGICナンバーも出てきますけど、最近はあんまり気を使わなくなりました。番号が直接書いてあるほうが良い場合もあるということにしましょう。
これで大抵のXML要素のパースが可能になりました。
| |trackbacks(4) |22:03 2010/11/01 |
iPhoneシミュレータでは表示されるのに、実機にインストールすると画像が表示されない…。
そんなことが何度かありました。今回はUIImageViewでそんな現象に遭遇しました。
原因は不明(実機のイメージキャッシュかな?)ですが、以下の手順で正常に表示されるようになりました。
不思議なことに、これでちゃんと表示されるようになりました。
| |trackbacks(2) |21:01 |