У меня запущено приложение на 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];
}
… который вычисляет из недавнего сенсорного ввода новое вращение колеса. Здесь уже есть одна проблема производительности, которую я преследую в отдельном потоке: Core Core-Animation для iOS: проблемы производительности с матрицами CATransaction / Interpolating transform
Эта процедура также проверяет, выполнило ли колесо еще один оборот на 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 раза в секунду.
** создание слоя кнопки **
Последняя часть головоломки, я думаю, это посмотреть на содержимое кнопки. но я пробовал
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 кадров в секунду), пока колесо не остановится, в этот момент он выполняет эту медленную анимацию изменения размера ( @ <20fps). </p>
, хотя это дает сносный результат, кажется сумасшедшим, что мне приходится так жертвовать своим дизайном пользовательского интерфейса. Я уверен, что должен делать что-то не так.
РЕДАКТИРОВАТЬ: я только что попытался в качестве эксперимента изменить размеры кнопок вручную; т.е. вставьте обратный вызов ссылки на дисплей в каждую кнопку и динамически рассчитайте ожидаемый размер этого данного кадра, явно отключите анимацию с помощью CATransaction так же, как я делал с колесом, установите новую матрицу преобразования (преобразование масштаба, сгенерированное из ожидаемого размера). В добавление к этому я установил слой содержимого кнопок shouldRasterize = YES. поэтому следует просто масштабировать 12 битовых карт каждого кадра на один UIView, который сам вращается. Удивительно, но это очень медленно, это даже останавливает симулятор. Это определенно в 10 раз медленнее, чем делать это автоматически с помощью анимации ядра.