UIView クラスの外で CALayer を初期化できない理由はありますか?
質問
私は現在開発中の iPhone アプリケーションで CALayers を「スプライト」として使用しようとしています。私が読んだ限りでは、それがここで行うのが正しいことのように思えます。
UIView クラス内でレイヤーを設定すると、レイヤーを画面上に表示できます。NSLog(@"GameView initWithCoder");
NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"earth" ofType:@"png"];
earthImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
earthLayer = [CALayer layer];
[earthLayer setContents:(id)[earthImage CGImage]];
[earthLayer setBounds:CGRectMake(0, 0, 32, 32)];
[earthLayer setPosition:CGPointMake(50, 50)];
[earthLayer setName:@"earth"];
[[self layer] addSublayer:earthLayer];
ただし、UIView の外部で CALayer を初期化しようとすると、アプリケーションがクラッシュします。
- (void)loadImage:(NSString*)fileName
{
NSLog(@"GamePiece loadImage");
NSArray* fileNameParts = [fileName componentsSeparatedByString:@"."];
imageName = [[fileNameParts objectAtIndex:0] retain];
NSString* ext = [fileNameParts objectAtIndex:1];
NSString* imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:ext];
image = [[UIImage alloc] initWithContentsOfFile:imagePath];
layer = [CALayer layer];
[layer setContents:(id)[image CGImage]];
[layer setBounds:CGRectMake(0.0f, 0.0f, 32.0f, 32.0f)];
[layer setPosition:CGPointMake(50.0f, 50.0f)];
[layer setName:imageName];
[fileNameParts release], fileNameParts = nil;
[ext release], ext = nil;
}
NSLog(@"GameView initWithCoder");
earth = [[GamePiece alloc] init];
[earth loadImage:@"earth.png"];
[[self layer] addSublayer:[earth layer]];
明らかに私はここで何か間違ったことをしていますが、それが何になるのかわかりません。私はコア アニメーション プログラミング ガイドと iPhone アプリケーション プログラミング ガイドを読みましたが、CALayer の初期化方法については、ついでに言う以外にはまったく触れていないようです。
正しい方向に優しく押してもらうだけで十分です。iPhone のプログラミングに関して私が学んだ他のすべてのことについては、かなりよく理解できるようになりました。グラフィックス、レイヤー、アニメーションに関していくつか問題が発生しています。
前もって感謝します!
解決
layer
メソッドから取得したレイヤーを所有していないため、所有権を取得する必要があります。メソッドが戻った後、レイヤーの割り当てが解除され、クラッシュが発生しています。これを治すには:
layer = [[CALayer layer] retain]; // now we explicitly take ownership of this layer
また、現在所有しているため、レイヤーをリリースすることを忘れないでください:
// in your dealloc method
[layer release];
Cocoaのメモリ管理プログラミングガイドオブジェクトの所有時期とメモリ管理に関する責任の詳細については、
他のヒント
ジェイソン、お返事ありがとうございます!しかし、あなたの答えは私の問題の結果に影響を与えないようです。
実際には、答えは「ある」ですメモリ管理に関連する何か。レイヤーではなく、componentsSeparatedByString:を呼び出した後にNSArrayから取得した名前。 NSArrayをリリースしない場合、コードは正常に機能します。したがって、配列内のオブジェクトを使用する代わりに、配列内でNSStringのコピーを作成する必要があります。
あなたが提案するとおり、Cocoaのメモリ管理プログラミングガイドを読んでいないと言わざるを得ません。ただし、Cocoaで作成されたオブジェクトとその保持カウントに使用される標準について説明した他のいくつかの本を読んだことがあります。私は公平に理解していますが、どうやらまだ必要なほど良くはないようです。
文字列を配列からコピーする必要があるという疑いが本当にありましたが、UIViewで機能するときは、それが正しいと仮定しました。問題は、GamePieceオブジェクト内にいるように保持していなかったことです。
少なくとも正しい方向に見させてくれてありがとう!私はこのコードで本当に終わりました。
希望どおりに実行するには、コードを変更する方法を次に示します。で loadImage:
この行を使用して追加します UIImage
あなたはにロードしました earth
層:
self.content = 画像.CGImage;
CALayers
持っています contents
に設定できるプロパティ CGImage
Core Graphics 呼び出しを使用して画像をレイヤーのコンテキストに描画するという面倒な作業を行う必要はありません。
次に、親で UIView
追加できるサブクラス earth
次のようにビュー階層に追加します。
[self.layer addSublayer:earth];
ご了承ください earth
それ自体は CALayer
内容として画像があるため、それをサブレイヤーとして直接追加します。私がいつも忘れてしまうもう 1 つのことは、レイヤーの設定です。 frame
財産。それ以外の場合、幅と高さはゼロになるため、表示されません。
コード内でレイヤーを作成しましたが、そのレイヤーをビュー階層に追加していませんでした。ビュー (クラス UIView) とそのサブビューのツリーがあります。ツリーのルートは [UIWindow mainwindow] です (UIWindow は UIWindow のサブクラスであることに注意してください) UIView
. 。それぞれ UIView
があります layer
型のプロパティ CALayer
実際には、その目に見えるコンテンツが保持されます。 UIView
それは単なるラッパーです CALayer
タッチ操作能力などを追加します。
ここで欠けている重要な考え方は、画面上に何かを表示するには、UIWindow をルートとするビューのツリーの一部でなければならず、すべてのビューが表示されなければならないということです (つまり、彼らの hidden
に設定されたプロパティ NO
) トップレベルウィンドウと問題のレイヤーの間にあります。そうすることで初めて、レイヤーが画面上に表示されます。したがって、コードのどこかで、呼び出して、すでに表示されているもののサブレイヤーとしてレイヤーを追加する必要があります。 addSublayer:
.
あなたの場合、私は推測します earth
のインスタンスです CALayer
そしてその中で何をしたか loadImage:
メソッドは新規作成です CALayer
(への電話で [CALayer layer]
) 以内 earth
レイヤーを作成しましたが、その新しいレイヤーをどこにも追加しなかったため、表示されることはありませんでした。代わりにあなたがしたのは、 earth
レイヤーにはまだコンテンツがありませんが、 UIView
さんのレイヤー
[[自分自身のレイヤー] addSublayer:[地球レイヤー]];
この行の問題の 1 つは、あなたがこう考えているように見えることです。 layer
のプロパティです earth
. 。実は layer
のクラスメソッドです CALayer
これは、新しいインスタンスを作成するためのファクトリ メソッドです。 CALayer
. 。これは混乱を招くため、 [self layer]
実際には、のレイヤープロパティにアクセスしています UIView
実例。(私は仮定しています self
のインスタンスを指します UIView
サブクラス。)