我有一个有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