У меня мое приложение работает на моем iPad. но он работает очень плохо - я получаю ниже 15 кадров в секунду. может ли кто-нибудь помочь мне оптимизировать?
Это в основном колесо (полученное из UIView), содержащее 12 кнопок (из UIControl).
По мере того как пользователь вращает его, кнопки динамически расширяются и сжимаются (например, тот, который находится в положении "12 часов", всегда должен быть самым большим)
Итак, мое колесо содержит:
- (void) displayLinkIsCallingBack: (CADisplayLink *) dispLink
{
:
// using CATransaction like this goes from 14fps to 19fps
[CATransaction begin];
[CATransaction setDisableActions: YES];
// NEG, as coord system is flipped/fucked
self.transform = CGAffineTransformMakeRotation(-thetaWheel);
[CATransaction commit];
if (BLA)
[self rotateNotch: direction];
}
... который вычисляет с недавнего сенсорного ввода новое вращение колеса. Здесь уже есть одна проблема с производительностью, которую я преследую в отдельном потоке: iOS Core-Animation: проблемы производительности с матрицами преобразования CATransaction/Interpolating
Эта процедура также проверяет, завершило ли колесо еще одно вращение на 1/12, и если это так, то все 12 кнопок будут изменять размер:
// Wheel.m
- (void) rotateNotch: (int) direction
{
for (int i=0; i < [self buttonCount] ; i++)
{
CustomButton * b = (CustomButton *) [self.buttons objectAtIndex: i];
// Note that b.btnSize is a dynamic property which will calculate
// the desired button size based on the button index and the wheels rotation.
[b resize: b.btnSize];
}
}
Теперь для фактического кода изменения размера в кнопке .m:
// Button.m
- (void) scaleFrom: (float) s_old
to: (float) s_new
time: (float) t
{
CABasicAnimation * scaleAnimation = [CABasicAnimation animationWithKeyPath: @"transform.scale"];
[scaleAnimation setDuration: t ];
[scaleAnimation setFromValue: (id) [NSNumber numberWithDouble: s_old] ];
[scaleAnimation setToValue: (id) [NSNumber numberWithDouble: s_new] ];
[scaleAnimation setTimingFunction: [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut] ];
[scaleAnimation setFillMode: kCAFillModeForwards];
scaleAnimation.removedOnCompletion = NO;
[self.contentsLayer addAnimation: scaleAnimation
forKey: @"transform.scale"];
if (self.displayShadow && self.shadowLayer)
[self.shadowLayer addAnimation: scaleAnimation
forKey: @"transform.scale"];
size = s_new;
}
// - - -
- (void) resize: (float) newSize
{
[self scaleFrom: size
to: newSize
time: 1.];
}
Интересно, связана ли проблема с накладными расходами нескольких операций transform.scale, стоящими перед очередью - каждая модификация кнопки занимает полную секунду, и если я быстро вращаю колесо, я могу закрутить пару оборотов в секунду; это означает, что каждая кнопка получает размер 24 раза в секунду.
** создание слоя кнопки **
Последний фрагмент головоломки, на мой взгляд, - это посмотреть на кнопку contentsLayer. но я пробовал
contentsLayer.setRasterize = YES;
который должен эффективно хранить его как растровое изображение. поэтому с настройкой кода действует динамическое изменение размера 12 растровых изображений.
Я не могу поверить, что это налогообложение устройства за его пределами. однако основной инструмент анимации говорит мне иначе; в то время как я вращаю колесо (волоча пальцем по кругу), он сообщает ~ 15 кадров в секунду.
Это нехорошо: мне в конечном итоге нужно поместить текстовый слой внутри каждой кнопки, и это приведет к дальнейшему снижению производительности (... если я не использую параметр .setRasterize выше, и в этом случае он должен быть таким же).
Должно быть, я что-то делаю неправильно! но что?
EDIT: вот код, ответственный за создание слоя содержимого кнопки (т.е. формы с тенью):
- (CALayer *) makeContentsLayer
{
CAShapeLayer * shapeOutline = [CAShapeLayer layer];
shapeOutline.path = self.pOutline;
CALayer * contents = [CALayer layer];
// get the smallest rectangle centred on (0,0) that completely contains the button
CGRect R = CGRectIntegral(CGPathGetPathBoundingBox(self.pOutline));
float xMax = MAX(abs(R.origin.x), abs(R.origin.x+R.size.width));
float yMax = MAX(abs(R.origin.y), abs(R.origin.y+R.size.height));
CGRect S = CGRectMake(-xMax, -yMax, 2*xMax, 2*yMax);
contents.bounds = S;
contents.shouldRasterize = YES; // try NO also
switch (technique)
{
case kMethodMask:
// clip contents layer by outline (outline centered on (0, 0))
contents.backgroundColor = self.clr;
contents.mask = shapeOutline;
break;
case kMethodComposite:
shapeOutline.fillColor = self.clr;
[contents addSublayer: shapeOutline];
self.shapeLayer = shapeOutline;
break;
default:
break;
}
if (NO)
[self markPosition: CGPointZero
onLayer: contents ];
//[self refreshTextLayer];
//[contents addSublayer: self.shapeLayerForText];
return contents;
}
как вы можете видеть, я стараюсь всякий возможный подход, я пытаюсь использовать два метода для создания фигуры, и отдельно я переключаюсь .shouldRasterize
** компрометация дизайна пользовательского интерфейса для получения допустимой частоты кадров **
EDIT: теперь я попытался отключить динамическое изменение размера, пока колесо не опустится в новое положение и не установит wheel.setRasterize = YES. поэтому он эффективно вращает одиночный preberendered UIView (который, по общему признанию, занимает большую часть экрана) под пальцем (который он с радостью делает @~ 60 кадров в секунду), пока колесо не успокоится, и в этот момент он выполняет эту небольшую анимацию изменения размера ( @< 20fps).
в то время как это дает терпимый результат, кажется, что мне кажется, что я должен пожертвовать своим дизайном интерфейса таким образом. Я уверен, что я должен что-то делать неправильно.
EDIT: я просто попробовал в качестве эксперимента изменить размеры кнопок вручную; т.е. поставить обратный вызов на экранной линии в каждой кнопке и динамически рассчитать ожидаемый размер этого фрейма, явно отключить анимацию с помощью CATransaction так же, как и с колесом, установить новую матрицу преобразования (масштабное преобразование, генерируемое из ожидаемого размера). добавлено к этому, я установил слой содержимого кнопок shouldRasterize = YES. поэтому он должен просто масштабировать 12 растровых изображений каждого кадра на один UIView, который сам вращается. удивительно, что это медленно, это даже приводит к остановке симулятора. Это определенно в 10 раз медленнее, чем автоматическое использование функции анимации ядра.