Есть ли причина, по которой я не могу инициализировать CALayer вне класса UIView?

StackOverflow https://stackoverflow.com/questions/805265

  •  03-07-2019
  •  | 
  •  

Вопрос

Я пытался использовать 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];

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

Заранее спасибо!

Это было полезно?

Решение

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

layer = [[CALayer layer] retain]; // now we explicitly take ownership of this layer

Кроме того, не забудьте освободить слой, поскольку он теперь принадлежит вам:

// in your dealloc method
[layer release];

Вам следует просмотреть Руководство по программированию управления памятью для Cocoa для получения более подробной информации о том, когда вы владеете объектами и каковы ваши обязанности в отношении управления памятью.

Другие советы

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

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

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

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

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

Чтобы сделать то, что вы хотели, вот как вы можете изменить свой код.В 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 в качестве его корня, и все представления должны быть видимы (т.иметь их hidden свойство установлено в NO) между окном верхнего уровня и рассматриваемым слоем.Только тогда слой будет виден на экране.Итак, где-то в вашем коде вам нужно добавить свой слой в качестве подслоя чего-то, что уже видно, вызвав addSublayer:.

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

[[собственный слой] addSublayer:[слой земли]];

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top