2010-05-26 110 views
22

我有一个UIView子类,其CALayer上使用CAShapeLayer面具。面具使用独特的形状,三个圆角和剩余角落中的一个切出矩形。动画CALayer的面具尺寸变化

当我使用标准动画块调整我的UIView的大小时,UIView本身和它的CALayer调整得很好。然而,该面罩立即应用,这导致一些绘图问题。

我试过动画使用CABasicAnimation面具的大小调整,但没有任何运气得到的动画进行调整。

我可以以某种方式实现面罩上的动画效果调整?我需要摆脱面具吗?还是必须改变我目前绘制面具的方式(使用- (void)drawInContext:(CGContextRef)ctx)。

干杯,亚历克斯

回答

37

我找到了解决这个问题。其他答案部分正确,有帮助。

以下几点是要了解解决方案的重要:

  • Mask属性不能设置动画本身。
  • 因为面具是一个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

你介意发表一个例子吗? – 2017-11-09 07:03:59

+0

@rockyraccoon抱歉,这太旧了,我无法制作完整的示例。如果你有一个具体的问题,我可能会提供帮助。 – Kekoa 2017-12-20 01:53:48

5

的CALayer面具属性不是动画这就解释了你缺乏运气在这个方向的。

请问你的面具的绘制依赖于框架上的面膜/界限? (你能提供一些代码吗?)面具是否有needsDisplayOnBoundsChange属性集?

干杯, 科林

+0

我这里有同样的问题,是的,遮罩适合视图/图层的边界。它增加了圆角。该物业已设置。 – Krumelur 2012-02-21 16:20:44

2

面具参数没有设置动画,但你可以动画这是为屏蔽层...

如果动画CAShapeLayer的路径属性,应该动画面具。我可以验证这是从我自己的项目中运行的。不确定使用非矢量掩码。你是否尝试过动画蒙版的内容属性?

感谢, 乔恩

+0

你可以给我一些示例代码吗?我有CAShapeLayer,然后我调用了setPath,然后我设置为uiview setMask:shapeLayer ...并且它现在可以作为calayer使用。如何访问此路径并进行更改?谢谢! – Piotr 2011-09-15 10:33:49

+0

我确实可以动画具有蒙版的图层,但只能在删除将其耦合到UIView的委托之后进行动画。然后我可以顺利调整图层大小。但是,调整大小时,蒙版(圆角)不会更新。视图内容也没有被调整大小。 – Krumelur 2012-02-21 16:16:42

0

我找不到任何的程序化的解决方案,所以我只画有正确的形状和alpha值的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通过设置self.layer.mask到该形状层来屏蔽UIView

只要视图的大小发生变化,就要动画蒙版我覆盖-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'。 – 2015-07-17 10:01:25

+0

也值得与定时功能相匹配 – Sam 2016-03-06 18:17:02