増刷かかりました。
 買ってくれた人、どうもありがとう!
 ということで、感謝を込めて、おまけシリーズ。

 本ではNSTimerやUIViewのカスタム化の学習を目的(もちろんInstrumentsのProfiler機能紹介のためのサンプルでもある)として花火を打ち上げたわけだけど、UIViewだらけになるのを気にしなければ、NSTimerとか使わなくても、花火の火の玉ひとつひとつをUIViewにして、FireworkViewのサブビューにしてbeginAnimation使ってアニメーションさせることでも実現できるんですな。

$テン*シー*シー-a4

 ただ、さすがに火の玉ひとつにUIViewひとつ、てのはUIResponderチェーンとか負荷かけすぎでしょなわけでして、そんな時のために用意されたCALayerを今回使ってみます。

 CALayerっていうのは、UIViewに一個存在する表示用の層(レイヤー)なんですが

$テン*シー*シー-a0

 UIView同様にCALayer同士を入れ子にもできるんですな。

$テン*シー*シー-a5

 そのうえ矩形とか背景色も設定できたりして、まんまUIViewていうか、なんでわざわざUIViewはCALayerとか持ってるの?
 てことになるんですが、どうもUIViewは画面の表示をCALayerを使ってやってるみたいで、本で書いたdrawRect:メソッドは結局CALayerに描き込んでるっぽいんですわ。 
 UIViewのアニメーションはCALayerによって支えられてるんじゃないかと…

 画面出力用のパーツってとこでしょうか。
 わざわざ出力と断ったのは、CALayerはタッチ関係のイベントを感知しないわけでして、そこらへんはUIViewが対応してるみたいっす。その点でUIResponderチェーンとか絡まなくて軽快なわけです。
 ま、実際Appleのドキュメントでも、何百でもどんとこい!とか書かれてるし、期待してみようかと…

興味がわいた人は、まずはこれを見るのじゃ。日本語訳あります↓
Core Animationプログラミングガイド(PDF)

 というわけで、今回のサンプルは、単に火の玉が指の場所で広がるんじゃなく
 花火用のCALayerを作って、UIViewの持ってるレイヤーにaddSublayerして、打ち上げて、その中に火の玉用のCALayerをサブレイヤーとして配置して四方に分散させる

 というものです。

 下の図のように、入れ子になったCALayerの親側のCALayerが上に動く場合に

$テン*シー*シー-a1

 子供側のCALayerが横に動くと

$テン*シー*シー-a2

 結果的に子供側のCALayerは斜めに動くってのを利用してます。

$テン*シー*シー-a3

 使うテンプレートプロジェクトはWindow-based Application

$テン*シー*シー-2

 View-basedでもいいんですが、それは本でやってるので、今回はUIViewController使わないバージョンです。
 いきがけの駄賃にiPad/iPhoneユニバーサルにしちゃう。

$テン*シー*シー-3

 次にFireworkLayerView.m/hを追加。
 File>New>New File…メニュー選んで出てくるウィンドウでCocoa TouchのObjective-C class選びます。

テン*シー*シー-4

 で、Nextボタンで次ぎに出てくる画面でUIViewを選択。

テン*シー*シー-5

 最後の保存ダイアログでFireworkLayerViewと名前を付けておしまい。

$テン*シー*シー-6

 こんな感じ。

$テン*シー*シー-7

 で、今回はCALayer使うのでフレームワークの追加が必要です。
 プロジェクトアイコン選んでTARGETSのFireworkLayer、Build Phasesタブ選んで表示される画面でLinke Binary With Librariesのデスクロージャを開いて、そこにある「+」ボタン押しましょう。

$テン*シー*シー-8

 そうするとフレームワーク選択画面がでるんでQuartzCoreを選択してAddボタン。

$テン*シー*シー-9
検索ボックスに qu とかいれるとフィルタリングされて表示されるんで便利

 用意ができたら、まずはFireworkLayerAppDelegate.h.mの変更。FireworkLayerViewを作ってwindowにaddSubviewします。背景色は当然黒。

#import "FireworkLayerAppDelegate.h"
#import "FireworkLayerView.h"

@implementation FireworkLayerAppDelegate

@synthesize window=_window;

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
FireworkLayerView* v = [[FireworkLayerView alloc]
initWithFrame:self.window.bounds];
v.backgroundColor = [UIColor blackColor];
[self.window addSubview:v];
[v release];

[self.window makeKeyAndVisible];
return YES;
}


 これでFireworkLayerViewを使う準備ができたので、今度はFireworkLayerViewクラスの実装。まずはFireworkLayerView.hでlastPosを追加。

@interface FireworkLayerView : UIView {
CGPoint lastPos;
}

@end

 FireworkViewではstaticで用意したけど、今回はインスタンスメソッドにします。FireworkLayerView.m側はtouchesMoved:withEvent:メソッドの実装。指の動きを感知して花火打ち上げるのは同じなんだけど、今回はstarLayerてのをインスタンス化して、自分のレイヤーにaddSublayer:します。

/**
* 10ポイント以上指の移動があれば1発、花火を打ち上げる。
*/
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint pos = [(UITouch*)[touches anyObject] locationInView:self];
float dx = pos.x - lastPos.x;
float dy = pos.y - lastPos.y;
if ((dx * dx + dy * dy) < 10*10) {
return;
}
lastPos = pos;
starLayer* star = [starLayer layer];
[star setpos:pos];
[self.layer addSublayer:star];

// 直に呼ぶとアニメーションがかからないのでこのようにする。
// ただし間をあける必要は無いので afterDelay:0 とする
[star performSelector:@selector(launch) withObject:nil afterDelay:0];
}

 今回の主役はこのstarLayerクラスでして、こいつはCALayerをカスタム化したクラスで、花火の核になります。でもって広がる火の玉をCALayerインスタンスとして20個作成し、自分のサブレイヤーとしてaddSublayer:します。
 そこらへんをやってるのが
-(void)setpos:(CGPoint)pos

 で、火の玉を広げるアニメーションを指定してるのが
-(void)bang

 ってことになります。

 本来CALayerのpositionやbackgroundColorプロパティは、設定すると自動的にアニメーションをともなって変更してくれるんで、
[CATransaction begin];
[CATransaction commit];

 で囲む必要はないんですが、アニメーションの時間を調整したい場合は、こうしないと駄目なんですな。
 あと、火の玉が予定した大きさまで広がったら、自分自身を親のレイヤーから切り離すためにアニメーション完了時に処理をするようにする
[CATransaction setCompletionBlock:^(void){
[self removeFromSuperlayer];
}];

 も必要だったわけです。
 ここで使ってる
^(void){…}
てのは、AppleではBlocksと読んでる、その場限りの一時的な関数を作れる機構なんですが、説明するとややこしいんで、がんばってドキュメント読んでください。
 なんかiOS 4.0からはこれでいくでー、ちゅー感じで、ばんばん使われてます。
 ま、実際便利だしな。

日本語訳あります↓
Blocksプログラミングトピックス(PDF)

 そんな感じで、あとは本の41ページで紹介したクイックヘルプ使って、調べてみてね~。

$テン*シー*シー-x
iPadでも動くお

------------
サンプルプロジェクト:FireworkLayer.zip