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

Анимировать границы CALayer w/redraws

Мне интересно, как можно анимировать границы CALayer, поэтому при каждом изменении границ слой вызывает drawInContext:. Я пробовал два следующих метода на моем подклассе CALayer:

  • Настройка needsDisplayOnBoundsChange на YES
  • Возврат YES для + (BOOL)needsDisplayForKey:(NSString*)key для клавиши bounds

Ни одна из них не работает. CALayer, похоже, решил использовать исходное содержимое слоя и просто масштабировать их в соответствии с contentsGravity (что, я полагаю, для производительности.) Является ли это обходным путем для этого или я пропущу что-то очевидное?

EDIT: И, кстати, я заметил, что мой пользовательский подкласс CALayer не вызывает initWithLayer: для создания presentationLayer - weird.

Спасибо заранее, Сэм

4b9b3361

Ответ 1

Вы можете использовать метод описанный здесь: переопределить метод CALayer +needsDisplayForKey:, и он будет перерисовывать его содержимое на каждом этапе анимации.

Ответ 2

Я не уверен, что установка viewFlags будет эффективной. Второе решение определенно не будет работать:

Реализация по умолчанию возвращает NO. Подклассы должны * называть супер для свойств, определяемых суперклассом. (Например, * не пытайтесь вернуть YES для свойств, реализованных CALayer, * делать будет имеют undefined результаты.)

Вам нужно установить режим содержимого представления в UIViewContentModeRedraw:

UIViewContentModeRedraw,    //redraw on bounds change (calls -setNeedsDisplay)

Ознакомьтесь с документацией Apple по предоставлению контента с помощью CALayer. Они рекомендуют использовать свойство делегата CALayer вместо подкласса, что может быть намного проще, чем то, что вы пытаетесь сейчас.

Ответ 3

Я не знаю, полностью ли это относится к решению вашего вопроса, но я смог заставить его работать.

Сначала я предварительно нарисовал свое содержимое в CGImageRef.

Затем я переопределяю метод -display моего слоя INSTEAD OF -drawInContext:. В нем я установил contents в предварительно обработанный CGImage, и он сработал.

Наконец, ваш слой также должен изменить значение по умолчанию contentsGravity на что-то вроде @"left", чтобы избежать масштабирования содержимого изображения.

Проблема, с которой я столкнулась, заключалась в том, что контекст, передаваемый в -drawInContext:, имел начальный размер слоя, а не окончательный размер после анимации. (Вы можете проверить это с помощью методов CGBitmapContextGetWidth и CGBitmapContextGetHeight.)

Мои методы по-прежнему вызывается только один раз для всей анимации, но установка содержимого слоя непосредственно с помощью метода -display позволяет передать изображение больше видимых границ. Метод drawInContext: не позволяет этого, так как вы не можете рисовать за пределами контекста CGContext.

Подробнее о различии между различными методами рисования слоев см. http://www.apeth.com/iOSBook/ch16.html

Ответ 4

В последнее время я сталкиваюсь с той же проблемой. Вот что я узнал:

Есть трюк, который вы можете сделать, это анимация теневой копии границ, например:

var shadowBounds: CGRect {
  get { return bounds }
  set { bounds = newValue}
}

затем переопределить CALayer + needsDisplayForKey:.

Однако это НЕ МОЖЕТ быть тем, что вы хотите сделать, если ваш рисунок зависит от границ. Как вы уже заметили, основная анимация просто масштабирует содержимое слоя до границы анимации. Это верно, даже если вы выполняете вышеупомянутый трюк, то есть содержимое масштабируется, даже если границы изменяются во время анимации. В результате ваша анимация чертежей выглядит непоследовательной.

Как его решить? Поскольку содержимое масштабируется, вы можете рассчитать значения пользовательских переменных, определяющих ваш рисунок, путем их обратного масштабирования, чтобы ваш рисунок на конечном, но масштабированном контенте выглядел так же, как исходный и немасштабированный, а затем установите значения fromValues ​​для этих значений, toValues ​​к их старым значениям, анимировать их в одно и то же время с границами. Если окончательные значения должны быть изменены, установите для toValues ​​эти конечные значения. Вы должны анимировать хотя бы одну настраиваемую переменную, чтобы вызвать перерисовку.

Ответ 5

Это ваш пользовательский класс:

@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...