2010-05-26 8 views
22

У меня есть подкласс UIView, который использует CAShapeLayer маску на своем CALayer. Маска использует четкую форму с тремя закругленными углами и вырезанным прямоугольником в остальном углу.Анимация изменения размера маски CALayer

Когда я изменяю размер моего UIView с использованием стандартного блока анимации, сам UIView и его размер CALayer просто отлично. Маска, однако, применяется мгновенно, что приводит к некоторым проблемам рисования.

Я пробовал анимировать изменение маски с помощью CABasicAnimation, но не повезло получить анимированный размер.

Могу ли я каким-то образом добиться анимированного эффекта изменения размера на маске? Нужно ли мне избавиться от маски, или мне нужно что-то изменить в том, как я рисую маску (используя - (void)drawInContext:(CGContextRef)ctx).

Приветствие, Alex

ответ

37

Я нашел решение этой проблемы. Другие ответы частично правильны и полезны.

Следующие пункты являются важными для понимания решения:

  • Маска свойство не Animatable себя.
  • Поскольку маска является CALayer, она может быть анимирована сама по себе.
  • Рамка не анимируется, используйте границы и положение. Это может не относиться к вам (если вы не пытались анимировать фрейм), но это было проблемой для меня. (См. Apple QA 1620)
  • Маска слоя обзора не привязана к UIView, поэтому она не получит транзакцию основной анимации, которая применяется к слою представления.
  • Мы модифицируем CALayer напрямую, поэтому мы не можем ожидать, что UIView будет иметь представление о том, что мы пытаемся сделать, поэтому анимация UIView не будет создавать транзакцию основной анимации для включения изменений в наши свойства.

Для того, чтобы решить проблему, нам придется напрямую использовать Core Animation и не можем полагаться на блок анимации UIView, чтобы выполнить эту работу для нас.

Просто создайте CATransaction с той же длительностью, в которой вы используете [UIView animateWithDuration:...]. Это создаст отдельную анимацию, но если ваша функция длительности и ослабления будет одинаковой, она должна анимироваться именно с другими анимациями в вашем блоке анимации.

NSTimeInterval duration = 0.5;// match this to the value of the UIView animateWithDuration: call 

[CATransaction begin]; 
[CATransaction setValue:[NSNumber numberWithFloat:duration] forKey:kCATransactionAnimationDuration]; 

self.myView.layer.mask.position = CGPointMake(newX, 0); 
self.myView.layer.mask.bounds = CGRectMake(0, 0, newWidth, newHeight); 

[CATransaction commit]; 
+0

Вы не представляете пример? –

+0

@rockyraccoon Извините, это слишком старо, я не могу представить полный пример. Если у вас есть конкретный вопрос, я могу помочь. – Kekoa

5

Маска свойство CALayer не анимируемое что объясняет ваше отсутствие удачи в этом направлении.

Находит ли рисунок вашей маски рамку/рамки маски? (Можете ли вы предоставить некоторый код?) Имеет ли в массе набор свойствDisplayOnBoundsChange?

Приветствия, Корин

+0

У меня есть такая же проблема здесь и да , маска соответствует границам вида/слоя. Это добавление округлых углов. Свойство установлено. – Krumelur

2

Параметр маска не живой, но вы можете оживить слой, который устанавливается в качестве маски ...

Если вы анимировать свойство Path в CAShapeLayer, в который должен оживить маска. Я могу проверить, что это работает из моих собственных проектов. Однако не уверен в использовании маски, отличной от вектора. Вы пробовали оживить свойство содержимого маски?

Спасибо, Jon

+0

Не могли бы вы дать мне пример кода? У меня есть CAShapeLayer, тогда я вызываю setPath на нем, тогда я настраиваю uiview setMask: shapeLayer ... и теперь он отображается как calayer. Как получить доступ к этому пути и изменить его? Благодаря! – Piotr

+0

Я действительно могу анимировать слой с маской, но только после удаления его делегата, который связывает его с UIView. Затем я могу плавно изменять размер слоя. Однако маска (закругленные углы) не будет обновляться при изменении размера. И ни один из них не может быть изменен. – Krumelur

0

я не мог найти решение, чтобы записать программное я просто рисовать PNG изображение с правильными значениями формы и альфа и использовать это вместо этого. Таким образом, мне не нужно использовать маску ...

0

Анимировать изменение маски можно.

Я предпочитаю использовать CAShapeLayer в качестве слоя маски. Очень удобно анимировать изменение маски с помощью пути свойств.

Перед анимацией любых изменений выгрузите содержимое источника в экземпляр CGImageRef и создайте новый слой для анимации. Скройте исходный слой во время анимации и покажите его, когда закончится анимация.

Ниже приведен пример кода для создания ключевой анимации по пути свойств. Если вы хотите создать свою собственную анимацию пути, убедитесь, что в пути всегда есть одинаковое количество точек.

- (CALayer*)_mosaicMergeLayer:(CGRect)bounds content:(CGImageRef)content isUp:(BOOL)isUp { 

    CALayer* layer = [CALayer layer]; 
    layer.frame = bounds; 
    layer.backgroundColor = [[UIColor clearColor] CGColor]; 
    layer.contents = (id)content; 

    CAShapeLayer* maskLayer = [CAShapeLayer layer]; 
    maskLayer.fillColor = [[UIColor blackColor] CGColor]; 
    maskLayer.frame = bounds; 
    maskLayer.fillRule = kCAFillRuleEvenOdd; 
    maskLayer.path = (isUp ? [self _maskArrowUp:-bounds.size.height*2] : [self _maskArrowDown:bounds.size.height*2]); 
    layer.mask = maskLayer; 

    CAKeyframeAnimation* ani = [CAKeyframeAnimation animationWithKeyPath:@"path"]; 
    ani.removedOnCompletion = YES; 
    ani.duration = 0.3f; 
    ani.fillMode = kCAFillModeForwards; 
    ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 

    NSArray* values = (isUp ? 
     [NSArray arrayWithObjects: 
     (id)[self _maskArrowUp:0], 
     (id)[self _maskArrowUp:-ceilf(bounds.size.height*1.2)], 
     nil] 
    :  
     [NSArray arrayWithObjects: 
     (id)[self _maskArrowDown:0], 
     (id)[self _maskArrowDown:bounds.size.height], 
     nil] 
    ); 
    ani.values = values; 
    ani.delegate = self; 
    [maskLayer addAnimation:ani forKey:nil]; 

    return layer; 
} 

- (void)_startMosaicMergeAni:(BOOL)up { 

    CALayer* overlayer = self.aniLayer; 
    CGRect bounds = overlayer.bounds; 
    self.firstHalfAni = NO; 

    CALayer* frontLayer = nil; 
    frontLayer = [self _mosaicMergeLayer:bounds 
           content:self.toViewSnapshot 
           isUp:up]; 
    overlayer.contents = (id)self.fromViewSnapshot; 
    [overlayer addSublayer:frontLayer]; 
} 
8

Я использую CAShapeLayer, чтобы замаскировать UIView путем установки self.layer.mask к этой форме слоя.

Чтобы оживить маску всякий раз, когда изменяется размер вида, я перезаписал -setBounds:, чтобы анимировать путь слоя маски, если границы изменяются во время анимации.

Вот как я реализовал его:

- (void)setBounds:(CGRect)bounds 
{ 
    [super setBounds:bounds]; 
    CAPropertyAnimation *boundsAnimation = (CABasicAnimation *)[self.layer animationForKey:@"bounds"]; 

    // update the mask 
    self.maskLayer.frame = self.layer.bounds; 

    // if the bounds change happens within an animation, also animate the mask path 
    if (!boundsAnimation) { 
     self.maskLayer.path = [self createMaskPath]; 
    } else { 
     // copying the original animation allows us to keep all animation settings 
     CABasicAnimation *animation = [boundsAnimation copy]; 
     animation.keyPath = @"path"; 

     CGPathRef newPath = [self createMaskPath]; 
     animation.fromValue = (id)self.maskLayer.path; 
     animation.toValue = (__bridge id)newPath; 

     self.maskLayer.path = newPath; 

     [self.maskLayer addAnimation:animation forKey:@"path"]; 
    } 
} 

(Для примера self.maskLayer установлен в `self.layer.mask)

Мои -createMaskPath вычисляет CGPathRef, который я использую, чтобы скрыть вид. Я также обновляю путь маски в -layoutSubviews.

3

Чтобы анимировать изменения границ маски слоя UIView: подкласс UIView и анимировать маску с CATransaction - подобный Kekodas ответ, но более общий:

@implementation UIMaskView 

- (void) layoutSubviews { 
    [super layoutSubviews]; 

    CAAnimation* animation = [self.layer animationForKey:@"bounds"]; 
    if (animation) { 
     [CATransaction begin]; 
     [CATransaction setAnimationDuration:animation.duration]; 
    } 

    self.layer.mask.bounds = self.layer.bounds; 
    if (animation) { 
     [CATransaction commit]; 
    } 
} 

@end 
+0

Ключ должен быть 'bounds.size', а не' bounds'. –

+0

также стоит сопоставить функцию синхронизации – Sam

Смежные вопросы