У меня есть приложение, работающее на моем 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];
}
}
Теперь о фактическом коде изменения размера, в button.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 раза в секунду.
** создание слоя кнопки **
Последний кусок головоломки, который я думаю, - это взглянуть на ContentLayer кнопки. но я пробовал
contentsLayer.setRasterize = YES;
, который должен эффективно сохранять его как растровое изображение. таким образом, с этой настройкой код фактически динамически изменяет размер 12 растровых изображений.
Я не могу поверить, что это чрезмерно нагружает устройство. однако инструмент основной анимации говорит мне об обратном; пока я вращаю колесо (проводя пальцем по кругу), он показывает ~ 15 кадров в секунду.
Это бесполезно: мне в конечном итоге нужно поместить текстовый слой внутри каждой кнопки, и это еще больше снизит производительность ( ... если я не использую параметр .setRasterize выше, в этом случае он должен быть таким же).
Я что-то делаю не так! но что?
РЕДАКТИРОВАТЬ: вот код, отвечающий за создание слоя содержимого кнопки (т.е. формы с тенью):
- (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
**, ставя под угрозу дизайн пользовательского интерфейса, чтобы получить приемлемую частоту кадров **
РЕДАКТИРОВАТЬ: Теперь я попытался отключить динамическое изменение размера, пока колесо не установится в новое положение, и установив wheel.setRasterize = YES. поэтому он эффективно вращает один предварительно отрендеренный UIView (который, по общему признанию, занимает большую часть экрана) под пальцем (что он с радостью делает @ ~ 60 кадров в секунду), пока колесо не остановится, после чего он выполняет эту медленную анимацию изменения размера ( @
Хотя это дает приемлемый результат, кажется безумием, что мне приходится жертвовать своим дизайном пользовательского интерфейса таким образом. Я уверен, что что-то делаю неправильно.
РЕДАКТИРОВАТЬ: Я просто в качестве эксперимента попытался изменить размер кнопок вручную; т.е. поместите обратный вызов ссылки отображения в каждой кнопке и динамически вычислите ожидаемый размер данного кадра, явно отключите анимацию с помощью CATransaction, так же, как я сделал с колесом, установите новую матрицу преобразования (масштабное преобразование, созданное из ожидаемого размера). к этому я установил слой содержимого кнопок shouldRasterize = YES. поэтому нужно просто масштабировать 12 растровых изображений в каждом кадре на один UIView, который сам вращается. что удивительно, это очень медленно, даже симулятор останавливается. Это определенно в 10 раз медленнее, чем при автоматическом использовании базовой анимации » s функция анимации.