Ответ 1
Вы можете использовать метод описанный здесь: переопределить метод CALayer +needsDisplayForKey:
, и он будет перерисовывать его содержимое на каждом этапе анимации.
Мне интересно, как можно анимировать границы CALayer, поэтому при каждом изменении границ слой вызывает drawInContext:
. Я пробовал два следующих метода на моем подклассе CALayer:
needsDisplayOnBoundsChange
на YES
YES
для + (BOOL)needsDisplayForKey:(NSString*)key
для клавиши bounds
Ни одна из них не работает. CALayer, похоже, решил использовать исходное содержимое слоя и просто масштабировать их в соответствии с contentsGravity
(что, я полагаю, для производительности.) Является ли это обходным путем для этого или я пропущу что-то очевидное?
EDIT: И, кстати, я заметил, что мой пользовательский подкласс CALayer
не вызывает initWithLayer:
для создания presentationLayer
- weird.
Спасибо заранее, Сэм
Вы можете использовать метод описанный здесь: переопределить метод CALayer +needsDisplayForKey:
, и он будет перерисовывать его содержимое на каждом этапе анимации.
Я не уверен, что установка viewFlags будет эффективной. Второе решение определенно не будет работать:
Реализация по умолчанию возвращает NO. Подклассы должны * называть супер для свойств, определяемых суперклассом. (Например, * не пытайтесь вернуть YES для свойств, реализованных CALayer, * делать будет имеют undefined результаты.)
Вам нужно установить режим содержимого представления в UIViewContentModeRedraw:
UIViewContentModeRedraw, //redraw on bounds change (calls -setNeedsDisplay)
Ознакомьтесь с документацией Apple по предоставлению контента с помощью CALayer. Они рекомендуют использовать свойство делегата CALayer вместо подкласса, что может быть намного проще, чем то, что вы пытаетесь сейчас.
Я не знаю, полностью ли это относится к решению вашего вопроса, но я смог заставить его работать.
Сначала я предварительно нарисовал свое содержимое в CGImageRef
.
Затем я переопределяю метод -display
моего слоя INSTEAD OF -drawInContext:
. В нем я установил contents
в предварительно обработанный CGImage, и он сработал.
Наконец, ваш слой также должен изменить значение по умолчанию contentsGravity
на что-то вроде @"left"
, чтобы избежать масштабирования содержимого изображения.
Проблема, с которой я столкнулась, заключалась в том, что контекст, передаваемый в -drawInContext:
, имел начальный размер слоя, а не окончательный размер после анимации. (Вы можете проверить это с помощью методов CGBitmapContextGetWidth
и CGBitmapContextGetHeight
.)
Мои методы по-прежнему вызывается только один раз для всей анимации, но установка содержимого слоя непосредственно с помощью метода -display
позволяет передать изображение больше видимых границ. Метод drawInContext:
не позволяет этого, так как вы не можете рисовать за пределами контекста CGContext.
Подробнее о различии между различными методами рисования слоев см. http://www.apeth.com/iOSBook/ch16.html
В последнее время я сталкиваюсь с той же проблемой. Вот что я узнал:
Есть трюк, который вы можете сделать, это анимация теневой копии границ, например:
var shadowBounds: CGRect {
get { return bounds }
set { bounds = newValue}
}
затем переопределить CALayer + needsDisplayForKey:.
Однако это НЕ МОЖЕТ быть тем, что вы хотите сделать, если ваш рисунок зависит от границ. Как вы уже заметили, основная анимация просто масштабирует содержимое слоя до границы анимации. Это верно, даже если вы выполняете вышеупомянутый трюк, то есть содержимое масштабируется, даже если границы изменяются во время анимации. В результате ваша анимация чертежей выглядит непоследовательной.
Как его решить? Поскольку содержимое масштабируется, вы можете рассчитать значения пользовательских переменных, определяющих ваш рисунок, путем их обратного масштабирования, чтобы ваш рисунок на конечном, но масштабированном контенте выглядел так же, как исходный и немасштабированный, а затем установите значения fromValues для этих значений, toValues к их старым значениям, анимировать их в одно и то же время с границами. Если окончательные значения должны быть изменены, установите для toValues эти конечные значения. Вы должны анимировать хотя бы одну настраиваемую переменную, чтобы вызвать перерисовку.
Это ваш пользовательский класс:
@implementation MyLayer
-(id)init
{
self = [super init];
if (self != nil)
self.actions = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNull null], @"bounds",
nil];
return self;
}
-(void)drawInContext:(CGContextRef)context
{
CGContextSetRGBFillColor(context,
drand48(),
drand48(),
drand48(),
1);
CGContextFillRect(context,
CGContextGetClipBoundingBox(context));
}
+(BOOL)needsDisplayForKey:(NSString*)key
{
if ([key isEqualToString:@"bounds"])
return YES;
return [super needsDisplayForKey:key];
}
@end
Это дополнения к шаблону по умолчанию xcode 4.2:
-(BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
// create and add layer
MyLayer *layer = [MyLayer layer];
[self.window.layer addSublayer:layer];
[self performSelector:@selector(changeBounds:)
withObject:layer];
return YES;
}
-(void)changeBounds:(MyLayer*)layer
{
// change bounds
layer.bounds = CGRectMake(0, 0,
drand48() * CGRectGetWidth(self.window.bounds),
drand48() * CGRectGetHeight(self.window.bounds));
// call "when idle"
[self performSelector:@selector(changeBounds:)
withObject:layer
afterDelay:0];
}
----------------- отредактирован:
Хорошо... это не то, о чем вы просили:) Извините: |
----------------- отредактировано (2):
И зачем вам нужно что-то подобное? (void)display
, но документация говорит, что он существует для установки self.contents
...