Анимация видов с помощью основного слоя анимации - PullRequest
8 голосов
/ 25 января 2011

У меня есть NSWindow, содержащий NSView с включенным 'Wants Core Animation Layer'. Представление тогда содержит много NSImageView, которые используют, первоначально анимированы в позицию. Когда я запускаю анимацию, она очень вялая и сбрасывает большинство кадров. Однако, если я отключу 'Wants Core Animation Layer', анимация будет работать отлично. Мне понадобится основной слой анимации, но я не могу понять, как заставить его работать адекватно.

Могу ли я что-нибудь сделать, чтобы исправить проблемы с производительностью?

Вот код:

// AppDelegate
NSRect origin = ...;
NSTimeInterval d = 0.0;
for (id view in views)
{
    [view performSelector:@selector(animateFrom:) withObject:origin afterDelay:d];
    d += 0.05f;
}

// NSImageView+Animations
- (void)animateFrom:(NSRect)origin
{   
    NSRect original = self.frame;
    [self setFrame:origin];
    [NSAnimationContext beginGrouping];
    [[NSAnimationContext currentContext] setDuration:0.20f];
    [[self animator] setFrame:original];
    [NSAnimationContext endGrouping];
}

Ответы [ 2 ]

10 голосов
/ 06 февраля 2011

Возможно, что NSTimer убивает вашу производительность.Core Animation имеет богатую поддержку для контроля времени анимации по протоколу CAMediaTiming, и вы должны воспользоваться этим в своем приложении.Вместо использования прокси аниматора и NSAnimationContext, попробуйте использовать Core Animation напрямую.Если вы создадите CABasicAnimation для каждого изображения и установите его beginTime, это приведет к задержке запуска анимации.Кроме того, чтобы задержка работала так, как вы хотите, вы должны обернуть каждую анимацию в CAAnimationGroup с ее duration, установленным на общее время всей анимации.

Использование свойства frame можеттакже будет способствовать замедлению.Мне действительно нравится использовать свойство transform в CALayer в таких ситуациях, когда вы делаете «открывающую» анимацию.Вы можете разместить свои изображения в IB (или в коде) на их конечных позициях, и непосредственно перед тем, как окно станет видимым, изменить их преобразования в исходную позицию анимации.Затем вы просто сбрасываете все преобразования на CATransform3DIdentity, чтобы вернуть интерфейс в нормальное состояние.

У меня есть пример в моем грядущая книга Core Animation это очень похоже на то, что вы пытаетесь сделать.Он оживляет 30 NSImageView с одновременно без пропущенных кадров.Я изменил пример для вас и поместил его на github .Вот наиболее важные фрагменты кода, в которых убраны посторонние элементы пользовательского интерфейса:

Преобразование слоев в их начальное положение

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  // ... SNIP ... //
  //Start with all of the images at the origin
  [CATransaction begin];
  [CATransaction setDisableActions:YES];
  for (CALayer *imageLayer in [[[self imageContainer] layer] sublayers]) {
    CGPoint layerPosition = [layer position];
    CATransform3D originTransform = CATransform3DMakeTranslation(20.f - layerPosition.x, -layerPosition.y, 0.f);
    [imageLayer setTransform:originTransform];
  }
  [CATransaction commit];
}

Анимация преобразования обратно к идентификатору

- (IBAction)runAnimation:(id)sender {
  CALayer *containerLayer = [[self imageContainer] layer];

  NSTimeInterval delay = 0.f;
  NSTimeInterval delayStep = .05f;
  NSTimeInterval singleDuration = [[self durationStepper] doubleValue];
  NSTimeInterval fullDuration = singleDuration + (delayStep * [[containerLayer sublayers] count]);

  for (CALayer *imageLayer in [containerLayer sublayers]) {
    CATransform3D currentTransform = [[imageLayer presentationLayer] transform];

    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
    anim.beginTime = delay;
    anim.fromValue = [NSValue valueWithCATransform3D:currentTransform];
    anim.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    anim.fillMode = kCAFillModeBackwards;
    anim.duration = singleDuration;

    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.animations = [NSArray arrayWithObject:anim];
    group.duration = fullDuration;

    [imageLayer setTransform:CATransform3DIdentity];
    [imageLayer addAnimation:group forKey:@"transform"];
    delay += delayStep;
  }
}

У меня также есть видео на YouTube примера в действии, если вы хотите его проверить.

1 голос
/ 28 января 2011

Вы пытались пакетировать все в CATransaction?

[CATransaction begin];
for {...}
[CATransaction commit];

CATransaction - это базовый механизм анимации для пакетной обработки нескольких операций дерева слоев в элементарных обновлениях дерева визуализации.

...