带有 UIBezierPath 的 CAShapeLayer 阴影 [英] CAShapeLayer Shadow with UIBezierPath

查看:73
本文介绍了带有 UIBezierPath 的 CAShapeLayer 阴影的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题从,但我不太确定如何将其添加到我的 UIBezierPath 正确:

let cap = CAShapeLayer()cap.shadowColor = UIColor.blackColor().CGColorcap.shadowRadius = 8.0cap.shadowOpacity = 0.9cap.shadowOffset = CGSize(width: 0, height: 0)cap.path = UIBezierPath(ovalInRect: CGRectMake(0, 40, 20, 20)).CGPathcap.fillColor = UIColor.grayColor().CGColorlayer.addSublayer(cap)

解决方案

我不知道这对您是否有用,因为它不使用 CHCircleGaugeView.我正在处理与这个问题相关的几个项目,所以我将它们混合在一起并进行了一些更改以生成一个进度视图,该视图具有颜色渐变背景,尖端与末端 100% 重叠.我还没有达到让圆形尖端在 0% 处消失的程度,但我最终会到达那里.以下是它在 2 个不同进度级别上的几个视图,

该视图是使用在 drawRect 中绘制的极坐标渐变创建的,并被环面遮蔽.圆形尖端是一个单独的层,它是一条线末端的半圆,连接到围绕其中心旋转的圆的中心,并根据进度级别进行变换.这是代码,

 @implementation AnnulusProgressView{//UIView 的子类int 切片;CGFloat circleRadius;CAShapeLayer *maskLayer;CGFloat 段角;CGFloat startAngle;CAShapeLayer *tipView;NSMutableArray *colors;整数标志;}-(instancetype)initWithFrame:(CGRect)frame WantsBackgroundRing:(BOOL)wantsBackground backgroundRingColor:(UIColor *)ringColor {if (self = [super initWithFrame:frame]) {切片 = 360;_ringThickness = 12;circleRadius = self.bounds.size.width/2;_startColor = [UIColor colorWithHue:0.24 饱和度:1 亮度:0.8 alpha:1];_endColor = [UIColor colorWithHue:0.03 饱和度:1 亮度:1 alpha:1];[自设置颜色数组];_backgroundRing = 想要背景?[self createBackgroundRingWithColor:ringColor] : nil;self.layer.mask = [self createAnnulusMask];}回归自我;}-(void)setStartColor:(UIColor *)startColor {_startColor = 开始颜色;[自设置颜色数组];}-(void)setEndColor:(UIColor *)endColor {_endColor = 结束颜色;[自设置颜色数组];}-(void)setupColorArray {颜色 = [NSMutableArray 新];CGFloat startHue, startSaturation, startBrightness, startAlpha, endHue, endSaturation, endBrightness, endAlpha;[self.startColor getHue:&startHue 饱和度:&startSaturation 亮度:&startBrightness alpha:&startAlpha];[self.endColor getHue:&endHue 饱和度:&endSaturation 亮度:&endBrightness alpha:&endAlpha];for(int i=0;i 1)?1 : _progress + (符号 * 0.1);if ((_progress > newValue && sign == 1) || (_progress < newValue && sign == -1)) {_progress = newValue;}maskLayer.strokeEnd = _progress;tipView.transform = CATransform3DMakeRotation(-(1 - _progress + 0.002) * M_PI*2, 0, 0, 1);//0.002 关闭了尖端和环面strokeEnd 之间的小间隙int i = (int)(_progress*(slices - 1));tipView.fillColor = ((UIColor *)colors[i]).CGColor;如果(符号== 1){如果(_progress < newValue){[self performSelector:@selector(animateProgressTo:) withObject:@(newValue) afterDelay:0.05];}}别的{如果(_progress > newValue){[self performSelector:@selector(animateProgressTo:) withObject:@(newValue) afterDelay:0.05];}}NSLog(@"%f",_progress);}- (CAShapeLayer *)createAnnulusMask {maskLayer = [CAShapeLayer 层];maskLayer.frame = self.bounds;segmentAngle = 2*M_PI/(切片);startAngle = M_PI_2;CGFloat endAngle = startAngle + 2*M_PI;maskLayer.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)) radius:circleRadius - self.ringThickness startAngle:startAngle endAngle:endAngle 顺时针:YES].CGPath;maskLayer.fillColor = [UIColor clearColor].CGColor;maskLayer.strokeColor = [UIColor blackColor].CGColor;maskLayer.lineWidth = self.ringThickness * 2;maskLayer.strokeEnd = self.progress;返回掩码层;}-(void)drawRect:(CGRect)rect{CGContextRef ctx = UIGraphicsGetCurrentContext();CGContextSetAllowsAntialiasing(ctx, NO);CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));for(int i=0;i

This question continues from a previous answer.

I have the following CAShapeLayer:

- (CAShapeLayer *)gaugeCircleLayer {

    if (_gaugeCircleLayer == nil) {
        _gaugeCircleLayer = [CAShapeLayer layer];
        _gaugeCircleLayer.lineWidth = self.gaugeWidth;
        _gaugeCircleLayer.fillColor = [UIColor clearColor].CGColor;
        _gaugeCircleLayer.strokeColor = self.gaugeTintColor.CGColor;
        _gaugeCircleLayer.strokeStart = 0.0f;
        _gaugeCircleLayer.strokeEnd = self.value;
        _gaugeCircleLayer.lineCap = kCALineCapRound;
        _gaugeCircleLayer.path = [self insideCirclePath].CGPath;

        CAShapeLayer *cap = [CAShapeLayer layer];
        cap.shadowColor = [UIColor blackColor].CGColor;
        cap.shadowRadius = 8.0;
        cap.shadowOpacity = 0.9;
        cap.shadowOffset = CGSizeMake(0, 0);
        cap.fillColor = [UIColor grayColor].CGColor;
        [_gaugeCircleLayer addSublayer:cap];
    }

    return _gaugeCircleLayer;
}

Then I have the following UIBezierPath:

- (UIBezierPath *)insideCirclePath {

    CGPoint arcCenter = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:arcCenter
                                                        radius:CGRectGetWidth(self.bounds) / 2.0f
                                                    startAngle:(3.0f * M_PI_2)
                                                      endAngle:(3.0f * M_PI_2) + (2.0f * M_PI)
                                                     clockwise:YES];


    return path;
}

This produces something like the following:

What I am trying to produce with the cap sublayer is the drop shadow at the end and I'd also be interested to know how to get a GradientLayer working similar to the image below:

The problem is that the sublayer is not appearing anywhere and I'm not quite sure why. I'm also not 100% sure on whether or not this is the best way to produce the desired effect.

UPDATE:

The following bit of code creates a cap, though I'm not quite sure how to add it to my UIBezierPath properly:

let cap = CAShapeLayer()
cap.shadowColor = UIColor.blackColor().CGColor
cap.shadowRadius = 8.0
cap.shadowOpacity = 0.9
cap.shadowOffset = CGSize(width: 0, height: 0)
cap.path = UIBezierPath(ovalInRect: CGRectMake(0, 40, 20, 20)).CGPath
cap.fillColor = UIColor.grayColor().CGColor
layer.addSublayer(cap)

解决方案

I don't know if this will be useful to you, since it doesn't use the CHCircleGaugeView. I was working on several projects related to this question, so I mashed them together and made some changes to produce a progress view that has a color gradient background with a tip that overlaps the end at 100%. I haven't gotten to the point where I make the rounded tip disappear at 0%, but I'll get there eventually. Here are a couple of views of it at 2 different progress levels,

The view is created with a polar gradient drawn in drawRect, masked by an annulus. The rounded tip is a separate layer that's a half circle on the end of a line connected to the center of the circle that's revolved around its center with a transform based on the progress level. Here's the code,

    @implementation AnnulusProgressView{ // subclass of UIView
    int slices;
    CGFloat circleRadius;
    CAShapeLayer *maskLayer;
    CGFloat segmentAngle;
    CGFloat startAngle;
    CAShapeLayer *tipView;
    NSMutableArray *colors;
    int sign;
}


-(instancetype)initWithFrame:(CGRect)frame wantsBackgroundRing:(BOOL)wantsBackground backgroundRingColor:(UIColor *)ringColor {
    if (self = [super initWithFrame:frame]) {
        slices = 360;
        _ringThickness = 12;
        circleRadius = self.bounds.size.width/2;
        _startColor = [UIColor colorWithHue:0.24 saturation:1 brightness:0.8 alpha:1];
        _endColor = [UIColor colorWithHue:0.03 saturation:1 brightness:1 alpha:1];
        [self setupColorArray];
        _backgroundRing = wantsBackground? [self createBackgroundRingWithColor:ringColor] : nil;
        self.layer.mask = [self createAnnulusMask];
    }
    return self;
}


-(void)setStartColor:(UIColor *)startColor {
    _startColor = startColor;
    [self setupColorArray];
}


-(void)setEndColor:(UIColor *)endColor {
    _endColor = endColor;
    [self setupColorArray];
}


-(void)setupColorArray {
    colors = [NSMutableArray new];
    CGFloat startHue, startSaturation, startBrightness, startAlpha, endHue, endSaturation, endBrightness, endAlpha;

    [self.startColor getHue:&startHue saturation:&startSaturation brightness:&startBrightness alpha:&startAlpha];
    [self.endColor getHue:&endHue saturation:&endSaturation brightness:&endBrightness alpha:&endAlpha];

    for(int i=0;i<slices;i++){
        CGFloat hue = startHue + (endHue - startHue)*i/slices;
        CGFloat brightness = startBrightness + (endBrightness - startBrightness)*i/slices;
        CGFloat saturation = startSaturation + (endSaturation - startSaturation)*i/slices;
        CGFloat alpha = startAlpha + (endAlpha - startAlpha)*i/slices;
        UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:alpha];
        [colors addObject:color];
    }
    self.progress = _progress;
}



-(UIView *)createBackgroundRingWithColor:(UIColor *) color {
    UIView *bgRing = [[UIView alloc] initWithFrame:self.frame];
    bgRing.backgroundColor = color;
    bgRing.layer.mask = [self createAnnulusMask];
    [(CAShapeLayer *)bgRing.layer.mask setStrokeEnd:startAngle + 2*M_PI ];
    return bgRing;
}



-(void)didMoveToSuperview {
    if (self.backgroundRing) [self.superview insertSubview:self.backgroundRing belowSubview:self];
    tipView = [self tipView];
    [self.superview.layer addSublayer:tipView];
}



-(CAShapeLayer *)tipView {
    CAShapeLayer *tip = [CAShapeLayer layer];
    tip.frame = self.frame;
    tip.fillColor = [UIColor redColor].CGColor;
    UIBezierPath *shape = [UIBezierPath bezierPath];
    CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
    [shape moveToPoint:center];
    CGPoint bottomPoint = CGPointMake(center.x, center.y + circleRadius - self.ringThickness*2);
    CGFloat tipCenterY = bottomPoint.y + self.ringThickness - 1;
    [shape addLineToPoint: bottomPoint];
    [shape addLineToPoint:CGPointMake(bottomPoint.x+2, bottomPoint.y)];
    double fractionAlongTangent = self.ringThickness;
    [shape addCurveToPoint:CGPointMake(center.x, center.y + circleRadius) controlPoint1:CGPointMake(center.x - self.ringThickness*1.5, tipCenterY - fractionAlongTangent) controlPoint2:CGPointMake(center.x - self.ringThickness*1.5, tipCenterY + fractionAlongTangent)];
    [shape closePath];
    tip.path = shape.CGPath;
    tip.shadowColor = [UIColor darkGrayColor].CGColor;
    tip.shadowOpacity = 0.8;
    tip.shadowOffset = CGSizeMake(-6, 0);
    tip.shadowRadius = 2;
    return tip;
}



- (void)setProgress:(CGFloat)progress{
    sign = (progress >= _progress)? 1 : -1;
    [self animateProgressTo:@(progress)];
}


-(void)animateProgressTo:(NSNumber *) newValueNumber{
    float newValue = newValueNumber.floatValue;
    _progress =  (_progress + (sign * 0.1) > 1)?  1 : _progress + (sign * 0.1);
    if ((_progress > newValue && sign == 1) || (_progress < newValue && sign == -1)) {
        _progress = newValue;
    }
    maskLayer.strokeEnd = _progress;
    tipView.transform =  CATransform3DMakeRotation(-(1 - _progress + 0.002) * M_PI*2, 0, 0, 1); //the 0.002 closes a small gap between the tip and the annulus strokeEnd
    int i = (int)(_progress*(slices - 1));
    tipView.fillColor = ((UIColor *)colors[i]).CGColor;
    if (sign == 1) {
        if (_progress < newValue) {
            [self performSelector:@selector(animateProgressTo:) withObject:@(newValue) afterDelay:0.05];
        }
    }else{
        if (_progress > newValue) {
            [self performSelector:@selector(animateProgressTo:) withObject:@(newValue) afterDelay:0.05];
        }
    }

    NSLog(@"%f",_progress);
}



- (CAShapeLayer *)createAnnulusMask {
    maskLayer = [CAShapeLayer layer];
    maskLayer.frame = self.bounds;

    segmentAngle = 2*M_PI/(slices);
    startAngle = M_PI_2;
    CGFloat endAngle = startAngle + 2*M_PI;

    maskLayer.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)) radius:circleRadius - self.ringThickness startAngle:startAngle  endAngle:endAngle clockwise:YES].CGPath;
    maskLayer.fillColor = [UIColor clearColor].CGColor;
    maskLayer.strokeColor = [UIColor blackColor].CGColor;
    maskLayer.lineWidth = self.ringThickness * 2;
    maskLayer.strokeEnd = self.progress;
    return  maskLayer;
}



-(void)drawRect:(CGRect)rect{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetAllowsAntialiasing(ctx, NO);
    CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));

    for(int i=0;i<slices;i++){
        CGContextSaveGState(ctx);
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:center];
        [path addArcWithCenter:center radius:circleRadius startAngle:startAngle endAngle:startAngle+segmentAngle clockwise:YES];
        [path addClip];
        [colors[i] setFill];
        [path fill];
        CGContextRestoreGState(ctx);

        startAngle += segmentAngle;
    }
}

这篇关于带有 UIBezierPath 的 CAShapeLayer 阴影的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆