Mac OS X のCore Animationを使ったアプリケーションを作成したときにつまずいたところをメモしておきます。
CIFilterを使って画像の一部分にフィルタをかけるアニメーションを実行したいのですが、フィルタをかける位置がずれてしまうのです。
次の様にawakeFromNibの中でCALayerのコンテンツを指定し、ビューに設定しています。
- (void)awakeFromNib { // レイヤーを作成する CALayer* layer; layer = [CALayer layer]; // レイヤーに画像をセットする NSURL *imageUrl = [NSURL fileURLWithPath: [[NSBundle mainBundle]pathForResource:@"map" ofType:@"png"]]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData]; layer.frame = CGRectMake(0, 0, [imageRep pixelsWide], [imageRep pixelsHigh]); layer.contents = (id)[imageRep CGImage]; layer.contentsGravity = kCAGravityCenter; // レイヤーをビューに設定する [imageView setLayer:layer]; [imageView setWantsLayer:YES]; }
画面の”done”ボタンを押すと画像の吹き出し部分にフィルタがかかるはずなのですが・・・・。
残念な感じに・・・。
awakeFromNibでCALayerのframe.size.widthとframe.size.heightにコンテンツに指定した画像の横と縦のピクセル数(353,278)を設定しています。しかし、調べてみると
[imageView setWantsLayer:YES];
の後、frame.size.widthとframe.size.heightの値がビューのframeの値(400,400)に変わっているのです。これが原因でした。
CIFilterではコンテンツに指定した画像の大きさを元にフィルタをかける位置を設定しています。ところが、レイヤーの大きさが画像の大きさと異なっていた為、フィルタを実行する位置がずれてしまったのです。
次の様にサブレイヤーに画像をセットしてフィルタをかけると、意図した位置でフィルタを実行することができました。
- (void)awakeFromNib { // レイヤーを作成する CALayer* layer; layer = [CALayer layer]; // レイアウトマネージャの設定 layer.layoutManager = [CAConstraintLayoutManager layoutManager]; // レイヤーをビューに設定する [imageView setLayer:layer]; [imageView setWantsLayer:YES]; // サブレイヤーを作成する CALayer *imageLayer = [CALayer layer]; // サブレイヤーに画像をセットする NSURL *imageUrl = [NSURL fileURLWithPath: [[NSBundle mainBundle]pathForResource:@"map" ofType:@"png"]]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData]; imageLayer.frame = CGRectMake(0, 0, [imageRep pixelsWide], [imageRep pixelsHigh]); imageLayer.contents = (id)[imageRep CGImage]; imageLayer.contentsGravity = kCAGravityCenter; // スーパーレイヤーの中心にサブレイヤーを表示する [imageLayer addConstraint: [CAConstraint constraintWithAttribute:kCAConstraintMidY relativeTo:@"superlayer" attribute:kCAConstraintMidY]]; [imageLayer addConstraint: [CAConstraint constraintWithAttribute:kCAConstraintMidX relativeTo:@"superlayer" attribute:kCAConstraintMidX]]; // レイヤーにサブレイヤーを追加する [layer addSublayer:imageLayer]; }
レイアウトマネージャを使用して、サブレイヤーの位置をスーパーレイヤーの位置を基準に決定しています。Core AnimationやレイヤについてはCore Animationプログラミングガイドが参考になりました。レイアウトマネージャについては、Core AnimationプログラミングガイドのCore Animationレイヤの配置を参考にしています。