Анимировать границы CALayer с перерисовками - PullRequest
16 голосов
/ 28 сентября 2011

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

  • Установка needsDisplayOnBoundsChange на YES
  • Возвращение YES для + (BOOL)needsDisplayForKey:(NSString*)key для bounds ключ

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

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

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

Ответы [ 5 ]

7 голосов
/ 31 октября 2011

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

1 голос
/ 31 октября 2011

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

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

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

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

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

0 голосов
/ 16 июля 2017

Недавно я столкнулся с той же проблемой.Это то, что я узнал:

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

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

, затем переопределение CALayer's + needsDisplayForKey:.

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

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

0 голосов
/ 05 ноября 2015

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

Сначала я нарисовал содержимое изображения в CGImageRef.

Затем я переопределил -display метод моего слоя ВМЕСТО -drawInContext:. В нем я установил contents для предварительно отрисованного CGImage, и это сработало.

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

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

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

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

0 голосов
/ 02 ноября 2011

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

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

...