以非常高的速度绘制动态形状iOS

时间:2016-05-31 19:39:24

标签: ios swift uibezierpath

我试图创造一种雷达"看起来像这样:

first radar

这个想法是蓝色部分将像指南针一样表示东西与用户的接近程度。当用户远离物镜时,形状将变窄,变长并呈蓝色;当接近物体时,形状会变短,变粗,变红,如下所示:

second radar

为了达到这个目的,我画了几个圈子(实际上我尝试了IBDesignable和IBInspectable,但我一直在"构建失败"但这是另一个主题)

我的问题是:这是否可以画出"锥形"应该以高速率改变其尺寸而不会有滞后风险的部分? 我可以使用UIBezierPath / AddArc或Addline方法来实现这一目标吗?或者我将走向死胡同?

2 个答案:

答案 0 :(得分:7)

有两种方法:

  1. 定义复杂的贝塞尔曲线形状并使用动画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"];
    }
    

    产量: enter image description here

  2. 另一种方法是使用简单的贝塞尔曲线,但使用显示链接进行动画处理:

    如果您想使用CAShapeLayerCADisplayLink(类似于针对屏幕更新而优化的计时器),您可以执行以下操作:

    @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;
    }
    

    产量: enter image description here

答案 1 :(得分:4)

您应该使用一个或多个CAShapeLayer个对象和核心动画。

您可以将CGPathUIBezierPath安装到形状图层中,然后您可以对形状图层进行动画处理,但为了让动画正常工作,您希望使用相同的编号和动画开头和结尾形状中的控制点类型。