Подтвердить что ты не робот

Базовая анимация анимации CALayer

Мы хотели использовать UITabBar в нашем приложении для iPhone, но за одним исключением: у нас есть кнопка "sync", которую я хотел бы вращать во время операции синхронизации.

alt text

К сожалению, это означало необходимость создания настраиваемой панели вкладок, но ни здесь, ни там: анимация, реализованная с использованием Core Animation, выглядит потрясающе. Проблема заключается в том, что во время анимации это отрицательно влияет на производительность всего остального, используя анимацию на экране: прокрутку UITableView, панорамирование и вывод папок MKMapView и т.д. Мое тестовое устройство - это iPhone 4.

Проблема заключается в том, как я реализовал панель вкладок - мне хотелось достичь чего-то очень похожего на UITabBar, где вы просто поставляете PNG для значка, и он использует альфа-канал для создания нормальных и выделенных состояний посредством маскирование фонового изображения. Я выполнил это с помощью свойства CALayer mask:

// Inside a UIView subclass' init method...

// Create the mask layer by settings its contents as our PNG icon.
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0, 0, 31, 31);
maskLayer.contentsGravity = kCAGravityCenter;
maskLayer.contentsScale = [[UIScreen mainScreen] scale];
maskLayer.rasterizationScale = [[UIScreen mainScreen] scale];
maskLayer.contents = (id)symbolImage.CGImage;
maskLayer.shouldRasterize = YES;
maskLayer.opaque = YES;

fgLayer = [[CALayer layer] retain];
fgLayer.frame = self.layer.frame;
fgLayer.backgroundColor = [UIColor colorWithImageNamed:@"tabbar-normal-bg.png"].CGColor;
fgLayer.mask = maskLayer; // Apply the mask
fgLayer.shouldRasterize = YES;
fgLayer.opaque = YES;

[self.layer addSublayer:fgLayer];

(Примечание: на скриншоте выше вы можете увидеть, что я добавил теневой слой, но для простоты я удалил это из кода. Я удаляю теневой слой из значка синхронизации при анимации, t быть релевантным.)

Чтобы оживить, я просто поворачиваю слой маски:

- (void)startAnimating {
    CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath: @"transform"];
    CATransform3D transform = CATransform3DMakeRotation(RADIANS(179.9), 0.0, 0.0, 1.0);
    animation.toValue = [NSValue valueWithCATransform3D:transform];
    animation.duration = 5;
    animation.repeatCount = 10000;
    animation.removedOnCompletion = YES;
    [fgLayer.mask addAnimation:animation forKey:@"rotate"]; // Add animation to the mask
}

Итак, все это отлично работает, кроме производительности. Вы можете видеть, что я уже пробовал советы, которые появились в Google о растрировании слоев/сделать их непрозрачными - не помогло.

Я думаю, что я определил слой маски как виновник. Когда я вынимаю слой маски и просто поворачиваю fgLayer вместо своей маски, производительность замечательная, хотя это, конечно, не влияет на меня:

alt text

Производительность также так же плоха, как и раньше, если я поворачиваю fgLayer вместо маски во время применения маски.

Итак, если нужно перекомпоновать маску, каждый кадр анимации замедляется, есть ли какие-либо другие методы, которые я могу использовать для достижения аналогичного эффекта, который будет иметь лучшую производительность? Использование пути вместо изображения для слоя маски? Или мне нужно будет спуститься в OpenGL или что-то, чтобы получить хорошую производительность?

UPDATE:, еще больше усиливая мысль о том, что маска является замедлением, мой коллега предложил попытаться повернуть CALayer только с изображением в виде содержимого - так похоже на мой пример выше w/oa mask - и производительность тоже была хорошей. Поэтому я могу действительно делать только такой сплошной цвет (без градиента), но это может быть хорошее временное решение. Я все равно буду любить добиваться вращения маски с хорошей производительностью, поэтому предложения приветствуются:)

4b9b3361

Ответ 1

Брент,

Зачем вам нужно использовать маску слоя? Не можете ли вы просто преобразовать слой маски в подслой? Вам просто нужно убедиться, что ваш образ имеет соответствующую альфу, и вы будете использовать его как CGImageRef в качестве содержимого этого слоя.

Еще одна вещь. Я еще не понял, почему, но я также заметил проблемы с производительностью, когда я применяю shouldRasterize на каждом уровне, а не только на верхнем уровне. Вы можете увидеть, удаляет ли ссылка на setShouldRasterize: ДА в вашем слое маски вообще помогает.

Ответ 2

Один из подходов заключался бы в создании CAShapeLayer для использования в качестве маски: у вас будет справедливая часть работы, которая будет вырезана для вас, создавая версию вашего значка "sync" с помощью дорожек Bézier, но слои формы несут гораздо более низкая стоимость исполнения за преобразование, чем растровые. К сожалению, вы не можете быть уверены в том, что ротация связана с проблемой производительности - вполне может быть маскирование, вызывающее большую часть задержки, и в этом случае вы сделаете все, что векторизация для небольшой выгоды.

Я думаю, что лучшим решением является использование возможностей анимации UIImage: создайте диафильм из каждого кадра анимации вращения и просто отобразите этот анимированный UIImage на панели вкладок. Это не самое изящное решение, но множество анимаций в системе - например, значок "Удалить" и "Удалить", и различные формы индикатора активности, например, реализованы таким же образом.