我试图创造一种雷达"看起来像这样:
这个想法是蓝色部分将像指南针一样表示东西与用户的接近程度。当用户远离物镜时,形状将变窄,变长并呈蓝色;当接近物体时,形状会变短,变粗,变红,如下所示:
为了达到这个目的,我画了几个圈子(实际上我尝试了IBDesignable和IBInspectable,但我一直在"构建失败"但这是另一个主题)
我的问题是:这是否可以画出"锥形"应该以高速率改变其尺寸而不会有滞后风险的部分? 我可以使用UIBezierPath / AddArc或Addline方法来实现这一目标吗?或者我将走向死胡同?
答案 0 :(得分:7)
有两种方法:
定义复杂的贝塞尔曲线形状并使用动画CABasicAnimation
:
- (UIBezierPath *)pathWithCenter:(CGPoint)center angle:(CGFloat)angle radius:(CGFloat)radius {
UIBezierPath *path = [UIBezierPath bezierPath];
CGPoint point1 = CGPointMake(center.x - radius * sinf(angle), center.y - radius * cosf(angle));
[path moveToPoint:center];
[path addLineToPoint:point1];
NSInteger curves = 8;
CGFloat currentAngle = M_PI_2 * 3.0 - angle;
for (NSInteger i = 0; i < curves; i++) {
CGFloat nextAngle = currentAngle + angle * 2.0 / curves;
CGPoint point2 = CGPointMake(center.x + radius * cosf(nextAngle), center.y + radius * sinf(nextAngle));
CGFloat controlPointRadius = radius / cosf(angle / curves);
CGPoint controlPoint = CGPointMake(center.x + controlPointRadius * cosf(currentAngle + angle / curves), center.y + controlPointRadius * sinf(currentAngle + angle / curves));
[path addQuadCurveToPoint:point2 controlPoint:controlPoint];
currentAngle = nextAngle;
point1 = point2;
}
[path closePath];
return path;
}
我只是将弧线分成一系列四边形曲线。我发现三次曲线可以越来越近,但只有几条四条曲线,我们得到了非常好的近似值。
然后使用CABasicAnimation
:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.fillColor = self.startColor.CGColor;
layer.path = self.startPath.CGPath;
[self.view.layer addSublayer:layer];
self.radarLayer = layer;
}
- (IBAction)didTapButton:(id)sender {
self.radarLayer.path = self.finalPath.CGPath;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
pathAnimation.fromValue = (id)self.startPath.CGPath;
pathAnimation.toValue = (id)self.finalPath.CGPath;
pathAnimation.removedOnCompletion = false;
self.radarLayer.fillColor = self.finalColor.CGColor;
CABasicAnimation *fillAnimation = [CABasicAnimation animationWithKeyPath:@"fillColor"];
fillAnimation.fromValue = (id)self.startColor.CGColor;
fillAnimation.toValue = (id)self.finalColor.CGColor;
fillAnimation.removedOnCompletion = false;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = @[pathAnimation, fillAnimation];
group.duration = 2.0;
[self.radarLayer addAnimation:group forKey:@"bothOfMyAnimations"];
}
另一种方法是使用简单的贝塞尔曲线,但使用显示链接进行动画处理:
如果您想使用CAShapeLayer
和CADisplayLink
(类似于针对屏幕更新而优化的计时器),您可以执行以下操作:
@interface ViewController ()
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic) CFAbsoluteTime startTime;
@property (nonatomic) CFAbsoluteTime duration;
@property (nonatomic, weak) CAShapeLayer *radarLayer;
@end
@implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
CAShapeLayer *layer = [CAShapeLayer layer];
[self.view.layer addSublayer:layer];
self.radarLayer = layer;
[self updateRadarLayer:layer percent:0.0];
}
- (IBAction)didTapButton:(id)sender {
[self startDisplayLink];
}
- (void)updateRadarLayer:(CAShapeLayer *)radarLayer percent:(CGFloat)percent {
CGFloat angle = M_PI_4 * (1.0 - percent / 2.0);
CGFloat distance = 200 * (0.5 + percent / 2.0);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:self.view.center];
[path addArcWithCenter:self.view.center radius:distance startAngle:M_PI_2 * 3.0 - angle endAngle:M_PI_2 * 3.0 + angle clockwise:true];
[path closePath];
radarLayer.path = path.CGPath;
radarLayer.fillColor = [self colorForPercent:percent].CGColor;
}
- (UIColor *)colorForPercent:(CGFloat)percent {
return [UIColor colorWithRed:percent green:0 blue:1.0 - percent alpha:1];
}
- (void)startDisplayLink {
self.startTime = CFAbsoluteTimeGetCurrent();
self.duration = 2.0;
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)handleDisplayLink:(CADisplayLink *)link {
CGFloat percent = (CFAbsoluteTimeGetCurrent() - self.startTime) / self.duration;
if (percent < 1.0) {
[self updateRadarLayer:self.radarLayer percent:percent];
} else {
[self stopDisplayLink];
[self updateRadarLayer:self.radarLayer percent:1.0];
}
}
- (void)stopDisplayLink {
[self.displayLink invalidate];
self.displayLink = nil;
}
答案 1 :(得分:4)
您应该使用一个或多个CAShapeLayer
个对象和核心动画。
您可以将CGPath
从UIBezierPath
安装到形状图层中,然后您可以对形状图层进行动画处理,但为了让动画正常工作,您希望使用相同的编号和动画开头和结尾形状中的控制点类型。