无法让CALayer更新其drawLayer:DURING一个边界动画

时间:2013-04-14 15:30:55

标签: ios objective-c animation core-animation core-graphics

我正在尝试为自定义UIView的边界设置动画,同时保持其图层与其父视图的大小相同。为此,我试图在其父视图旁边设置图层边界的动画。我需要图层来调用drawLayer:withContext AS它的动画,所以我的自定义绘图将正确地改变大小和边界。

正确调用

drawLayer并在开始动画之前正确绘制。但我不能让图层在边界动画的每一步调用它的drawLayer方法。相反,它只是将其称为ONCE,立即跳到动画最后一帧的“结束边界”。

// self.bg is a property pointing to my custom UIView
self.bg.layer.needsDisplayOnBoundsChange = YES;
self.bg.layer.mask.needsDisplayOnBoundsChange = YES;

[UIView animateWithDuration:2 delay:0 options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{

    [CATransaction begin];
    self.bg.layer.bounds = bounds;
    self.bg.layer.mask.bounds = bounds;
    [CATransaction commit];

    self.bg.bounds = bounds;
} completion:nil];

为什么边界不会报告其动画的变化(而不仅仅是最终帧)?我做错了什么?

3 个答案:

答案 0 :(得分:3)

这可能会有所帮助,也可能没有帮助......

许多人都不知道Core Animation有一个supercool功能,允许您以可以设置动画的方式定义自己的图层属性。我使用的一个例子是给CALayer子类一个thickness属性。当我使用Core Animation制作动画时......

CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"thickness"];
ba.toValue = @10.0f;
ba.autoreverses = YES;
[lay addAnimation:ba forKey:nil];

...效果是在整个动画中重复调用drawInContext:drawLayer:...,允许我按照当前thickness属性值(动画过程中的中间值)。

在我看来,可能就是你所追求的那种东西。如果是这样,您可以在此处找到可下载的示例:

https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch17p498customAnimatableProperty

讨论(来自我的书):

http://www.apeth.com/iOSBook/ch17.html#_making_a_property_animatable

答案 1 :(得分:1)

这是因为您要绘制的图层与屏幕上显示的图层不同。

当您为图层属性设置动画时,它将立即设置为模型图层中的最终值(正如您所注意到的),并且实际动画在表示层中完成。

您可以访问表示层并查看动画属性的实际值:

CALayer *presentationLayer = (CALayer *)[self.bg.layer presentationLayer];
...

由于您尚未提供drawLayer:withContext方法,因此不清楚您希望在动画期间绘制什么内容,但如果您想为自定义属性设置动画,请here is a good tutorial执行此操作。

答案 2 :(得分:1)

首先,图层支持(或托管)视图的图层始终调整大小以适合其父视图的边界。如果将视图设置为图层委托,则视图将在每个帧接收drawLayer:inContext:。当然,您必须确保如果您的图层有needsDisplayOnBoundsChange == YES

以下是调整窗口大小的示例(在Mac上),然后更改底层的路径。

// My Nib contains one view and one button. 
// The view has a MPView class and the button action is resizeWindow:

@interface MPView() {
    CAShapeLayer     *_hostLayer;
    CALayer          *_outerLayer;
    CAShapeLayer     *_innerLayer;
}
@end

@implementation MPView

- (void)awakeFromNib
{
    [CATransaction begin];
    [CATransaction setDisableActions:YES];

    _hostLayer = [CAShapeLayer layer];
    _hostLayer.backgroundColor = [NSColor blackColor].CGColor;
    _hostLayer.borderColor = [NSColor redColor].CGColor;
    _hostLayer.borderWidth = 2;
    _hostLayer.needsDisplayOnBoundsChange = YES;
    _hostLayer.delegate = self;
    _hostLayer.lineWidth = 4;
    _hostLayer.strokeColor = [NSColor greenColor].CGColor;
    _hostLayer.needsDisplayOnBoundsChange = YES;

    self.layer = _hostLayer;
    self.wantsLayer = YES;

    [CATransaction commit];

    [self.window setFrame:CGRectMake(100, 100, 200, 200) display:YES animate:NO];
}

- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
    if (layer == _hostLayer) {

        CGSize size = layer.bounds.size;
        CGMutablePathRef path = CGPathCreateMutable();

        CGPathMoveToPoint(path, NULL, 0, 0);
        CGPathAddLineToPoint(path, NULL, size.width, size.height);

        _hostLayer.path = path;

        CGPathRelease(path);
    }
}

- (IBAction)resizeWindow:(id)sender
{
    [self.window setFrame:CGRectMake(100, 100, 1200, 800) display:YES animate:YES];
}

@end