2009-04-30 2 views
3

Я пытался использовать CALayers как «спрайты» в приложении для iPhone, над которым я работаю. Из того, что я читал, это похоже на правильную вещь здесь.Есть ли причина, по которой я не могу инициализировать CALayer вне класса UIView?

Когда я настраиваю слой внутри класса 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]; 

Однако, когда я пытаюсь инициализировать CALayer за пределами UIView, мой сбой приложения:

- (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. У меня просто проблемы с графикой, слоями и анимацией.

Заранее благодарен!

ответ

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 для более подробной информации о том, когда у вас есть объекты и то, что ваш обязанности связаны с управлением памятью.

0

Джейсон, большое вам спасибо за отзыв! Однако ваш ответ, похоже, не влияет на исход моей проблемы.

Собственно, ответ «есть» что-то связано с управлением памятью. Не слой, но имя, которое я получил от NSArray после вызова компонентовSeparatedByString :. Если я не выпущу NSArray, код работает нормально. Поэтому мне нужно сделать копию NSString внутри массива вместо использования объекта в массиве.

Должен сказать, что я не читал Руководство по программированию управления памятью для какао, как вы предлагаете. Тем не менее, я прочитал несколько других книг, в которых говорилось о стандарте, используемом в Cocoa для созданных объектов и их количествах. У меня есть справедливое понимание, но, видимо, не так хорошо, как нужно.

У меня действительно было подозрение, что я должен скопировать строку из массива, но когда она работала в UIView, я просто предположил, что это правильно. Проблема заключалась в том, что я не держал ее так, как будто я внутри объекта GamePiece.

Благодарим вас за то, что заставило меня посмотреть в правильном направлении! Я действительно был в курсе этого кода.

+1

На самом деле, слой по-прежнему является проблемой, даже если вам повезло. Вы должны сохранить его, если это ивар. Что касается имени файла, я пропустил это.Вам фактически не нужно сохранять или копировать это. Просто не выпускайте его :) В основном, вещи, которые вы используете только во время своей функции, не должны активно управляться, если вы их не создали (alloc/copy/new). Вещи, которые вы хотите придерживаться, должны быть активно управляемы (поэтому, если вы не выделили/не скопировали/не новый, вы должны сохранить). –

0

Чтобы сделать то, что вам нужно, вот как вы можете изменить свой код.В loadImage: использовать эту линию, чтобы добавить UIImage загруженной в earth слой:

self.contents = image.CGImage;

CALayers имеет contents свойство, которое вы можете установить на CGImage, а не через неприятность с помощью Core Graphics требует, чтобы сделать изображение в контекст слоя.

Тогда в родительском UIView подкласса вы можете добавить earth в иерархии представлений, как это:

[self.layer addSublayer: земли];

earth Обратите внимание, что само по себе является CALayer с изображением, как его содержание, таким образом я добавить его непосредственно в качестве подслоя. Другая вещь, которую я всегда забываю сделать, - установить свойство frame слоя. В противном случае его ширина и высота будут равны нулю и поэтому не будут отображаться.

В коде вы создали слой, но не добавили свой слой в иерархию представлений. Существует дерево, если view (класс UIView) и их подпункты. Корень дерева [UIWindow MainWindow] (обратите внимание, что UIWindow является подклассом UIView. Каждый UIView имеет layer свойство типа CALayer, который фактически держит его видимое содержание. UIView просто обертка CALayer, чтобы добавить такие вещи, как обработка сенсорной способность

Важная идея, которую вы здесь не видите, заключается в том, что для того, чтобы что-либо было видимым на экране, оно должно быть частью этого дерева представлений с использованием UIWindow в качестве его корня, и все виды должны быть видимыми (т. Е. Иметь hidden: NO) между окном верхнего уровня и рассматриваемым слоем. Только тогда слой будет виден на экране. Поэтому где-то в коде вам нужно добавить свой слой в качестве подуровня сома что уже видно по телефону addSublayer:.

В вашем случае, я предполагаю, что earth является экземпляром CALayer и то, что вы сделали внутри метода loadImage: это создать новый CALayer (с вызовом [CALayer layer]) в пределах earth слоя, но тогда вы никогда не добавил, что новый слой для чего-либо, чтобы он никогда не становился видимым. То, что вы сделали вместо этого попытка добавить earth слой, который до сих пор не имеет никакого содержания, к тому, что я предполагаю, что это слой толщиной UIView «s

[[само слой] addSublayer: [земля слой]];

Одна проблема с этой линией, кажется, вы думаете layer является собственностью earth. Фактически layer - это метод класса CALayer, который является заводским методом для создания новых экземпляров CALayer. Это запутывает, потому что [self layer] фактически получает доступ к свойству layer экземпляра UIView. (Я предполагаю, что self относится к экземпляру подкласса UIView.)