ios – CALayer的动画效果与UIView动画相同

我有一个有16个子图层的类来形成徽标.当我为UIView设置动画时,CALayers不会被动画化,而是像下面的动画gif一样进入最终状态:

我的代码是:

@implementation logoView

#define CGRECTMAKE(a,b,w,h) {.origin={.x=(a),.y=(b)},.size={.width=(w),.height=(h)}}

#pragma mark - Create Subviews
const static CGRect path[] = {
        CGRECTMAKE(62.734375,-21.675000,18.900000,18.900000),CGRECTMAKE(29.784375,-31.725000,27.400000,27.300000),CGRECTMAKE(2.534375,-81.775000,CGRECTMAKE(4.384375,-57.225000,CGRECTMAKE(2.784375,62.875000,CGRECTMAKE(4.334375,29.925000,CGRECTMAKE(62.734375,2.525000,4.475000,CGRECTMAKE(-21.665625,CGRECTMAKE(-31.765625,CGRECTMAKE(-81.615625,-21.425000,CGRECTMAKE(-57.215625,-31.775000,2.775000,4.425000,CGRECTMAKE(-21.415625,27.300000)
    };

- (void) createSubviews
{

    self.contentMode = UIViewContentModeRedraw;
    for (int i = 0; i < 16; i++) {
        CGRect rect = CGRectApplyAffineTransform(path[i],CGAffineTransformMakeScale(self.frame.size.width / 213.0,self.frame.size.height / 213.0));
        UIBezierPath * b = [UIBezierPath bezierPathWithovalInRect:
          CGRectOffset(rect,self.frame.size.width/2.0,self.frame.size.height/2)];
        CAShapeLayer * layer = [[CAShapeLayer alloc] init];
        layer.path = [b CGPath];
        layer.fillColor = [self.tintColor CGColor];
        [self.layer addSublayer:layer];
    }
    self.layer.needsdisplayOnBoundsChange = YES;
    self.initialLenght = self.frame.size.width;
}

- (void) layoutSublayersOfLayer:(CALayer *)layer
{
    for (int i = 0; i < 16; i++) {
        CGRect rect = CGRectApplyAffineTransform(path[i],CGAffineTransformMakeScale(layer.frame.size.width / 213.0,layer.frame.size.height / 213.0));
        UIBezierPath * b = [UIBezierPath 
            bezierPathWithovalInRect:CGRectOffset(rect,layer.frame.size.width/2.0,layer.frame.size.height/2)];
        ((CAShapeLayer*)(layer.sublayers[i])).path = b.CGPath;
    }
}

- (void)layoutSubviews {
    [super layoutSubviews];

    // get current animation for bounds
    CAAnimation *anim = [self.layer animationForKey:@"bounds"];

    [CATransaction begin];
    if(anim) {
        // animating,apply same duration and timing function.
        [CATransaction setAnimationDuration:anim.duration];
        [CATransaction setAnimationTimingFunction:anim.timingFunction];

        CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
        [self.layer addAnimation:pathAnimation forKey:@"path"];
    }
    else {
        // not animating,we should disable implicit animations.
        [CATransaction disableActions];
    }
    self.layer.frame = self.frame;
    [CATransaction commit];
}

我正在制作动画:

[UIView animateWithDuration:3.0 animations:^{
        [v setFrame:CGRectMake(0.0,0.0,300.0,300.0)];
    }];

如何将图层动画与视图动画同步?

解决方法

与UIView动画同步动画图层属性可能很棘手.相反,让我们使用不同的视图结构来利用内置的支持来动画视图的变换,而不是尝试为图层的路径设置动画.

我们将使用两个视图:superview和subview. superview是一个logoView,是我们在故事板中展示的(或者你创建的UI). logoView为类logoLayerView添加一个子视图.这个logoLayerView使用CAShapeLayer作为其图层而不是普通的CALayer.

请注意,我们只需要一个CAShapeLayer,因为路径可以包含多个断开连接的区域.

我们将logoLayerView的帧/边界设置为CGRectMake(0,213,213),并且永远不会更改它.相反,当外部logoView更改大小时,我们设置logoLayerView的变换,以便它仍然填充外部logoView.

这是结果:

这是代码

logoView.h

#import <UIKit/UIKit.h>

IB_DESIGNABLE
@interface logoView : UIView

@end

logoView.m

#import "logoView.h"

#define CGRECTMAKE(a,.height=(h)}}

const static CGRect ovalRects[] = {
    CGRECTMAKE(62.734375,27.300000)
};

#define logoDimension 213.0

@interface logoLayerView : UIView
@property (nonatomic,strong,readonly) CAShapeLayer *layer;
@end

@implementation logoLayerView

@dynamic layer;

+ (Class)layerClass {
    return [CAShapeLayer class];
}

- (void)layoutSubviews {
    [super layoutSubviews];
    if (self.layer.path == nil) {
        [self initShapeLayer];
    }
}

- (void)initShapeLayer {
    self.layer.backgroundColor = [UIColor yellowColor].CGColor;
    self.layer.strokeColor = nil;
    self.layer.fillColor = [UIColor greenColor].CGColor;

    UIBezierPath *path = [UIBezierPath bezierPath];
    for (size_t i = 0; i < sizeof ovalRects / sizeof *ovalRects; ++i) {
        [path appendpath:[UIBezierPath bezierPathWithovalInRect:ovalRects[i]]];
    }
    [path applyTransform:CGAffineTransformMakeTranslation(logoDimension / 2,logoDimension / 2)];
    self.layer.path = path.CGPath;
}

@end

@implementation logoView {
    logoLayerView *layerView;
}

- (void)layoutSubviews {
    [super layoutSubviews];

    if (layerView == nil) {
        layerView = [[logoLayerView alloc] init];
        layerView.layer.anchorPoint = CGPointZero;
        layerView.frame = CGRectMake(0,logoDimension,logoDimension);
        [self addSubview:layerView];
    }
    [self layoutShapeLayer];
}

- (void)layoutShapeLayer {
    CGSize mySize = self.bounds.size;
    layerView.transform = CGAffineTransformMakeScale(mySize.width / logoDimension,mySize.height / logoDimension);
}

@end

相关文章

UITabBarController 是 iOS 中用于管理和显示选项卡界面的一...
UITableView的重用机制避免了频繁创建和销毁单元格的开销,使...
Objective-C中,类的实例变量(instance variables)和属性(...
从内存管理的角度来看,block可以作为方法的传入参数是因为b...
WKWebView 是 iOS 开发中用于显示网页内容的组件,它是在 iO...
OC中常用的多线程编程技术: 1. NSThread NSThread是Objecti...