我一直在尝试将CALayers作为我正在研发的iPhone应用程序中的“精灵”。从我一直在阅读的内容来看,这似乎是正确的事情。
当我在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时,我对其他所有学到的东西都有了很好的理解。我在图形,图层和动画方面遇到了一些问题。
提前致谢!
答案 0 :(得分:2)
您不拥有从layer
方法获得的图层,因此您必须拥有所有权。在您的方法返回之后的某个时间,该层正在被释放,这导致您的崩溃。解决这个问题:
layer = [[CALayer layer] retain]; // now we explicitly take ownership of this layer
另外,不要忘记现在拥有它,因为它已经拥有了它:
// in your dealloc method
[layer release];
您应该查看Memory Management Programming Guide for Cocoa以获取有关何时拥有对象的更多详细信息以及您对内存管理的责任。
答案 1 :(得分:0)
实际上,答案“是”与内存管理相关的东西。不是Layer,而是在调用componentsSeparatedByString后从NSArray获得的名称。如果我不释放NSArray,代码工作正常。所以我需要在数组中复制NSString而不是使用数组中的对象。
我不得不说我没有按照你的建议阅读Cocoa内存管理编程指南。但是,我已经阅读了其他几本书,其中讨论了Cocoa中用于创建对象的标准及其保留计数。我有一个公平的理解,但显然,还没有那么好。
我真的怀疑我应该将字符串复制出数组,但是当它在UIView中工作时,我只是认为它是正确的。问题是,我没有保持它像我在GamePiece对象中。
感谢您至少让我朝着正确的方向前进!我真的很擅长这段代码。
答案 2 :(得分:0)
要做你想做的事,你可以在这里改变你的代码。在loadImage:
中使用此行将您加载的UIImage
添加到earth
图层中:
self.contents = image.CGImage;
CALayers
具有contents
属性,您可以将其设置为CGImage
,而不是经历使用Core Graphics调用将图像绘制到图层上下文中的麻烦。
然后在父UIView
子类中,您可以将earth
添加到视图层次结构中,如下所示:
[self.layer addSublayer:earth];
请注意,earth
本身是CALayer
,其中包含图像作为内容,因此我将其直接添加为子图层。我总是忘记做的另一件事是设置图层的frame
属性。否则它的宽度和高度将为零,因此不会显示。
在您的代码中,您创建了一个图层,但从未将图层添加到视图层次结构中。如果视图(类UIView)及其子视图有一棵树。树的根是[UIWindow mainwindow](请注意,UIWindow是UIView
的子类。每个UIView
都有layer
类型CALayer
的属性,它实际上保持其可见内容。UIView
只是CALayer
的一个包装,可以添加触摸处理功能等内容。
你在这里缺少的重要想法是,对于屏幕上可见的任何内容,它必须是这个视图树的一部分,以UIWindow为根,并且所有视图必须是可见的(即具有它们的{{ 1}}属性设置为hidden
)在顶层窗口和相关图层之间。只有这样才能在屏幕上看到一个图层。因此,在代码中的某个位置,您需要通过调用NO
将图层添加为已经可见的内容的子图层。
在您的情况下,我假设addSublayer:
是earth
的一个实例,您在CALayer
方法中所做的是创建一个新的loadImage:
(在CALayer
图层内调用[CALayer layer]
},但之后您从未将新图层添加到任何内容中,因此它永远不会显示。您所做的是尝试将earth
图层添加到我认为是earth
图层
[[self layer] addSublayer:[earth layer]];
这一行的一个问题是您似乎认为UIView
是layer
的属性。实际上earth
是layer
的类方法,它是一种创建CALayer
新实例的工厂方法。这很令人困惑,因为CALayer
实际上正在访问[self layer]
实例的图层属性。 (我假设UIView
指的是self
子类的实例。)