概要:
CALayer
的position
白話來講:position
就是layer 自身的anchorPoint
相對于父superLayer
的坐標位置嘹朗。
常規(guī)說來师妙,在iOS上,一個圖層的position位于父圖層的左上角
具體概念參考:這將是你最后一次糾結(jié)position與anchorPoint
關(guān)于畫曲線的角度參考圖:
正文:
需求是一個掃描匹配的動畫:
如上展示的是最終的效果屹培。這里主要用的是一個自定義view
PulsingView
默穴,重寫 (void)drawRect:(CGRect)rect
方法怔檩,在這個方法內(nèi)部去畫相應(yīng)的元素,主要包含:
-
1.外部擴散的脈沖layer蓄诽。
-
2.雷達掃面的內(nèi)圈layer薛训。
-
3.雷達掃描的扇形layer。
首先是外部的脈沖擴散動畫實現(xiàn):
這個比較簡單仑氛,參考網(wǎng)上的好多雷達掃描實現(xiàn)乙埃,PulsingView.m
文件中 代碼如下:
[super drawRect:rect];
[[UIColor whiteColor] setFill];
UIRectFill(rect);
UIImage * image = [UIImage imageNamed:@"radar_background"];
[image drawInRect:rect];
CGFloat pulsingWidth = rect.size.width / 2.5;
CGFloat pulsingX = self.center.x - pulsingWidth * 0.5;
CGFloat pulsingY = self.center.y - pulsingWidth * 0.5;
// 脈沖layer
CALayer * pulsingLayer = [CALayer layer];
pulsingLayer.frame = CGRectMake(pulsingX, pulsingY, pulsingWidth, pulsingWidth);
pulsingLayer.borderColor = [UIColor colorWithRed:201/255.0 green:201/255.0 blue:201/255.0 alpha:1].CGColor;
pulsingLayer.cornerRadius = pulsingWidth * 0.5;
pulsingLayer.backgroundColor = [UIColor whiteColor].CGColor;//這里必須設(shè)置layer層的背景顏色,默認應(yīng)該是透明的调衰,導(dǎo)致設(shè)置的陰影顏色無法顯示出來
pulsingLayer.shadowColor = [UIColor blackColor].CGColor;
pulsingLayer.shadowOffset = CGSizeMake(0, 0);
pulsingLayer.shadowOpacity = 1;
pulsingLayer.shadowRadius = 5;
[self.layer addSublayer:pulsingLayer];
// 縮放動畫
CABasicAnimation * scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.autoreverses = false;
scaleAnimation.fromValue = @1.0f;
scaleAnimation.toValue = @2.0f;
// 透明動畫
CAKeyframeAnimation * opacityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
opacityAnimation.keyTimes = @[@0,@0.1,@0.2,@0.3,@0.4,@0.5,@0.6,@0.7,@0.8,@0.9,@1];
opacityAnimation.values = @[@1,@0.9,@0.8,@0.7,@0.6,@0.5,@0.4,@0.3,@0.2,@0.1,@0];
CAAnimationGroup * animationGroup = [CAAnimationGroup animation];
animationGroup.beginTime = CACurrentMediaTime() + 1;
animationGroup.fillMode = kCAFillModeBackwards;
animationGroup.repeatCount = HUGE_VAL;
animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animationGroup.duration = 2;
animationGroup.animations = @[scaleAnimation,opacityAnimation];
[pulsingLayer addAnimation:animationGroup forKey:@"pulse"];
這一步做完會出來如下效果:
接下來就是添加雷達的內(nèi)圈layer,繼續(xù)在上面的代碼下面添加如下代碼:
// 中間圓環(huán)
CALayer * middleCycle = [CALayer layer];
middleCycle.frame = CGRectMake(pulsingX, pulsingY, pulsingWidth, pulsingWidth);
middleCycle.cornerRadius = pulsingWidth * 0.5;
middleCycle.borderColor = [UIColor colorWithRed:41/255.0 green:152/255.0 blue:241/255.0 alpha:1].CGColor;
middleCycle.backgroundColor = [UIColor whiteColor].CGColor;
middleCycle.shadowOffset = CGSizeMake(0, 0);
middleCycle.borderWidth = 1;
middleCycle.shadowRadius = 3;
middleCycle.shadowOpacity = 1;
middleCycle.shadowColor = [UIColor lightGrayColor].CGColor;
[self.layer addSublayer:middleCycle];
膊爪。這里的坑也是出現(xiàn)在這塊地方。因為在外部引用PulsingView
的時候嚎莉,設(shè)置的frame
是控制器view
的bounds
,此時運行出來的結(jié)果如下:
接下來就是最后一步米酬,添加扇形的layer 并添加掃描動畫(一開始扇形是打算做成漸變的效果的,不過這里沒有完美實現(xiàn)出來趋箩,一開始使用了一個三方XHRadarView,單純的引用里面的扇形view的類赃额,這樣就可以直接使用他內(nèi)部的漸變color,不過出來并不是很完美叫确,所以還是自己畫扇形跳芳,先不做漸變效果了。)此時就出現(xiàn)了關(guān)于position問題導(dǎo)致的坑.
這里一開的思路還是和之前一樣竹勉,繼續(xù)在self.layer
上添加扇形的layer飞盆,以self.center
為圓心,畫一個90度的曲線:
CGPoint fanshapedCenter = self.center;
UIBezierPath * fanshapedPath = [UIBezierPath bezierPath];
[fanshapedPath addArcWithCenter:fanshapedCenter radius:(pulsingWidth + 20) * 0.5 startAngle:3 * M_PI / 2 endAngle:0 clockwise:YES];
[fanshapedPath addLineToPoint:fanshapedCenter];
[fanshapedPath closePath];
CAShapeLayer * fanshapedLayer = [CAShapeLayer layer];
fanshapedLayer.path = fanshapedPath.CGPath;
fanshapedLayer.opacity = 0.7;
fanshapedLayer.fillColor = [UIColor colorWithRed:41/255.0 green:152/255.0 blue:241/255.0 alpha:1].CGColor;
[self.layer addSublayer:fanshapedLayer];
此時運行出來靜態(tài)情況下的都沒問題:
然后添加旋轉(zhuǎn)動畫次乓,讓扇形繞著圓心進行自轉(zhuǎn):
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: 3 * M_PI ];
rotationAnimation.duration = 8;
rotationAnimation.cumulative = YES;
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.repeatCount = MAXFLOAT;
rotationAnimation.fillMode = kCAFillModeForwards;
[fanshapedLayer addAnimation:rotationAnimation forKey:@"rotation"];
然而運行出來的結(jié)果并沒有和我們預(yù)期的一樣:
仔細觀察可以發(fā)現(xiàn)吓歇,扇形是在圍繞著self的原點(0,0)點進行公轉(zhuǎn),一開始一直以為是畫扇形的時候出的問題票腰,是不是畫法不對之類的城看,還試過直接用Quaritz2D 去畫扇形,然而效果依然如此杏慰,經(jīng)過各種查找测柠,才發(fā)現(xiàn)是position 導(dǎo)致的問題,通過斷點缘滥,打印一下
hanshapedLayer
可以看到如下信息:
Printing description of fanshapedLayer:
<CAShapeLayer:0x608000233640; position = CGPoint (0 0); bounds = CGRect (0 0; 0 0); allowsGroupOpacity = YES; fillColor = <CGColor 0x6080000bef00> [<CGColorSpace 0x600000228940> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)] ( 0.160784 0.596078 0.945098 1 ); opacity = 0.7; path = <CGPath 0x60800003bf20>>
可以看到hanshapedLayer的position 是 (0,0),也就是父控件的左上角原點轰胁,文章開頭我們提到過,layer的position 就是自己anchorPoint 相對于父layer 的坐標朝扼,而layer自身旋轉(zhuǎn)也是相對于anchorPoint 旋轉(zhuǎn)软吐,這也就解釋了為什么我們上面會出現(xiàn)扇形繞著屏幕的最上角做公轉(zhuǎn)。所以我們就需要將hanshapedLayer
的 position 移到中心點的位置:
fanshapedLayer.position = fanshapedCenter;
此時我們再來看運行結(jié)果:
此時扇形確實是繞著中心點開始轉(zhuǎn)了吟税,但是并不是我們想要的結(jié)果凹耙。姿现。。此時內(nèi)心是崩潰的肖抱。所以放棄了這種動畫的方式备典,直接將扇形fanshapedLayer
添加到中間的內(nèi)圈layer上,然后讓內(nèi)圈layer自轉(zhuǎn)
意述,最后可以實現(xiàn)最終的效果提佣。
// 掃描的扇形
UIBezierPath * fanshapedPath = [UIBezierPath bezierPath];
[fanshapedPath addArcWithCenter:CGPointMake(middleCycle.frame.size.width * 0.5, middleCycle.frame.size.height * 0.5) radius:(pulsingWidth + 30) * 0.5 startAngle:3 * M_PI / 2 endAngle:0 clockwise:YES];
[fanshapedPath addLineToPoint:CGPointMake(middleCycle.frame.size.width * 0.5, middleCycle.frame.size.height * 0.5)];
CAShapeLayer * fanshaped = [CAShapeLayer layer];
fanshaped.fillColor = [UIColor colorWithRed:41/255.0 green:152/255.0 blue:241/255.0 alpha:1].CGColor;
fanshaped.opacity = 0.7;
[fanshapedPath closePath];
fanshaped.path = fanshapedPath.CGPath;
[middleCycle addSublayer:fanshaped];
然后將動畫添加到middleCycle上:
[middleCycle addAnimation:rotationAnimation forKey:@"rotation"];
后來在簡書上又看到以篇博客也遇到了公轉(zhuǎn)的問題 CALayer旋轉(zhuǎn)動畫
參考這位 渣孩子
博主的作法,將畫扇形的代碼作如下修改:
[fanshapedPath addArcWithCenter:CGPointZero radius:(pulsingWidth + 20) * 0.5 startAngle:3 * M_PI / 2 endAngle:0 clockwise:YES];
[fanshapedPath addLineToPoint:CGPointZero];
現(xiàn)在原點位置畫出扇形荤崇,然后再設(shè)置potisionfanshapedLayer.position = fanshapedCenter;
最后終于解決了這個問題拌屏。
最后,貼上draw 方法里的全部代碼:
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
[super drawRect:rect];
[[UIColor whiteColor] setFill];
UIRectFill(rect);
UIImage * image = [UIImage imageNamed:@"radar_background"];
[image drawInRect:rect];
CGFloat pulsingWidth = rect.size.width / 2.5;
CGFloat pulsingX = self.center.x - pulsingWidth * 0.5;
CGFloat pulsingY = self.center.y - pulsingWidth * 0.5;
// 脈沖layer
CALayer * pulsingLayer = [CALayer layer];
pulsingLayer.frame = CGRectMake(pulsingX, pulsingY, pulsingWidth, pulsingWidth);
pulsingLayer.borderColor = [UIColor colorWithRed:201/255.0 green:201/255.0 blue:201/255.0 alpha:1].CGColor;
pulsingLayer.cornerRadius = pulsingWidth * 0.5;
pulsingLayer.backgroundColor = [UIColor whiteColor].CGColor;//這里必須設(shè)置layer層的背景顏色术荤,默認應(yīng)該是透明的倚喂,導(dǎo)致設(shè)置的陰影顏色無法顯示出來
pulsingLayer.shadowColor = [UIColor blackColor].CGColor;
pulsingLayer.shadowOffset = CGSizeMake(0, 0);
pulsingLayer.shadowOpacity = 1;
pulsingLayer.shadowRadius = 5;
[self.layer addSublayer:pulsingLayer];
// 縮放動畫
CABasicAnimation * scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.autoreverses = false;
scaleAnimation.fromValue = @1.0f;
scaleAnimation.toValue = @2.0f;
// 透明動畫
CAKeyframeAnimation * opacityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
opacityAnimation.keyTimes = @[@0,@0.1,@0.2,@0.3,@0.4,@0.5,@0.6,@0.7,@0.8,@0.9,@1];
opacityAnimation.values = @[@1,@0.9,@0.8,@0.7,@0.6,@0.5,@0.4,@0.3,@0.2,@0.1,@0];
CAAnimationGroup * animationGroup = [CAAnimationGroup animation];
animationGroup.beginTime = CACurrentMediaTime() + 1;
animationGroup.fillMode = kCAFillModeBackwards;
animationGroup.repeatCount = HUGE_VAL;
animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animationGroup.duration = 2;
animationGroup.animations = @[scaleAnimation,opacityAnimation];
[pulsingLayer addAnimation:animationGroup forKey:@"pulse"];
// 中間圓環(huán)
CALayer * middleCycle = [CALayer layer];
middleCycle.frame = CGRectMake(pulsingX, pulsingY, pulsingWidth, pulsingWidth);
middleCycle.cornerRadius = pulsingWidth * 0.5;
middleCycle.borderColor = [UIColor colorWithRed:41/255.0 green:152/255.0 blue:241/255.0 alpha:1].CGColor;
middleCycle.backgroundColor = [UIColor whiteColor].CGColor;
middleCycle.shadowOffset = CGSizeMake(0, 0);
middleCycle.borderWidth = 1;
middleCycle.shadowRadius = 3;
middleCycle.shadowOpacity = 1;
middleCycle.shadowColor = [UIColor lightGrayColor].CGColor;
[self.layer addSublayer:middleCycle];
// 添加到self.layer上
CGPoint fanshapedCenter = self.center;
UIBezierPath * fanshapedPath = [UIBezierPath bezierPath];
[fanshapedPath addArcWithCenter:CGPointZero radius:(pulsingWidth + 20) * 0.5 startAngle:3 * M_PI / 2 endAngle:0 clockwise:YES];
[fanshapedPath addLineToPoint:CGPointZero];
[fanshapedPath closePath];
CAShapeLayer * fanshapedLayer = [CAShapeLayer layer];
fanshapedLayer.path = fanshapedPath.CGPath;
fanshapedLayer.opacity = 0.7;
fanshapedLayer.position = fanshapedCenter;
fanshapedLayer.fillColor = [UIColor colorWithRed:41/255.0 green:152/255.0 blue:241/255.0 alpha:1].CGColor;
[self.layer addSublayer:fanshapedLayer];
// 添加到middleCycle上
// [fanshapedPath addArcWithCenter:CGPointMake(0,0) radius:(pulsingWidth + 20) * 0.5 startAngle:3 * M_PI / 2 endAngle:0 clockwise:YES];
// [fanshapedPath addLineToPoint:CGPointZero];
// CAShapeLayer * fanshaped = [CAShapeLayer layer];
// fanshaped.fillColor = [UIColor colorWithRed:41/255.0 green:152/255.0 blue:241/255.0 alpha:1].CGColor;
// fanshaped.opacity = 0.7;
// fanshaped.position = CGPointMake(middleCycle.frame.size.width * 0.5, middleCycle.frame.size.height * 0.5);
// [fanshapedPath closePath];
// fanshaped.path = fanshapedPath.CGPath;
// [middleCycle addSublayer:fanshaped];
// NSLog(@"fanshaped==== frame:%@,anchorPoint:%@,bounds:%@,position:%@",NSStringFromCGRect(fanshaped.frame),NSStringFromCGPoint(fanshaped.anchorPoint),NSStringFromCGRect(fanshaped.bounds),NSStringFromCGPoint(fanshaped.position));
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: 3 * M_PI ];
rotationAnimation.duration = 8;
rotationAnimation.cumulative = YES;
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.repeatCount = MAXFLOAT;
rotationAnimation.fillMode = kCAFillModeForwards;
// [middleCycle addAnimation:rotationAnimation forKey:@"rotation"];
[fanshapedLayer addAnimation:rotationAnimation forKey:@"rotation"];
}