draw embossed arc using core graphics

后端 未结 2 1462
北荒
北荒 2021-02-02 03:19

I am trying to implement a custom slider as shown in figure below.

\"enter

what I

2条回答
  •  野的像风
    2021-02-02 03:48

    Unless you are going to be changing the shape dynamically, you would probably be better off just creating the image in an image editor. I know it's easy to create that effect in Photoshop, Illustrator, or Fireworks.

    That said, drawing an inner shadow like that with Core Graphics requires several steps:

    1. Clip to the shape (using e.g. CGContextClip or CGContextClipToMask).
    2. Make a path or mask of everything but the shape.
    3. Set your shadow parameters (using CGContextSetShadowWithColor).
    4. Fill the path or mask from step 2. This casts a shadow inside the shape, and only the shadow is drawn because you clipped to the shape in step 1.

    If you do all of that correctly, you can get a nice result like this:

    screen shot of arc with inner shadow

    Here's the code I wrote to draw that. I wrote it in the drawRect: of a custom view subclass, but you can easily use this code to draw into any graphics context.

    - (void)drawRect:(CGRect)rect {
        CGContextRef gc = UIGraphicsGetCurrentContext();
    

    First, I create a path that's just an arc:

        static CGFloat const kArcThickness = 20.0f;
        CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
        CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
        CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
        UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];
    

    Next, I ask Core Graphics to make a new path that is the outline of the arc path. Note how I ask it for a stroke width of kArcThickness and round line caps:

        CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
    

    I also need the inverse of that path: a path that includes every point except the points in shape. It so happens (although I don't think it's documented) that CGContextCreateCopyByStrokingPath and CGPathAddRect draw in opposite directions. So if I copy shape and draw an enormous rectangle around it, the non-zero winding rule means that the new path will be the inverse of shape:

        CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
        CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
    

    Now I can actually start drawing. First, I'll fill in the shape with a light gray color:

        CGContextBeginPath(gc);
        CGContextAddPath(gc, shape);
        CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
        CGContextFillPath(gc);
    

    Next I actually perform the four steps I listed above. I have to save the graphics state so I can undo the clipping and shadow parameters when I'm done.

        CGContextSaveGState(gc); {
    

    Step 1: clip to the shape:

            CGContextBeginPath(gc);
            CGContextAddPath(gc, shape);
            CGContextClip(gc);
    

    Step 2: Well, I did this step already when I created shapeInverse.

    Step 3: I set the shadow parameters:

            CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
    

    Step 4: I fill the inverse shape from step 2:

            CGContextBeginPath(gc);
            CGContextAddPath(gc, shapeInverse);
            CGContextFillPath(gc);
    

    Now I restore the graphics state, which specifically restores the clipping path and unsets the shadow parameters.

        } CGContextRestoreGState(gc);
    

    Finally, I'll stroke shape with a light gray to make the edge crisper:

        CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
        CGContextSetLineWidth(gc, 1);
        CGContextSetLineJoin(gc, kCGLineCapRound);
        CGContextBeginPath(gc);
        CGContextAddPath(gc, shape);
        CGContextStrokePath(gc);
    

    Of course I clean up when I'm done:

        CGPathRelease(shape);
        CGPathRelease(shapeInverse);
    }
    

    For more complex shapes, you can look at my answer here and my answer here.

    Here's all the code together for easy copying:

    - (void)drawRect:(CGRect)rect {
        CGContextRef gc = UIGraphicsGetCurrentContext();
    
        static CGFloat const kArcThickness = 20.0f;
        CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
        CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
        CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
        UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];
        CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
        CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
        CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
    
        CGContextBeginPath(gc);
        CGContextAddPath(gc, shape);
        CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
        CGContextFillPath(gc);
    
        CGContextSaveGState(gc); {
            CGContextBeginPath(gc);
            CGContextAddPath(gc, shape);
            CGContextClip(gc);
            CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
            CGContextBeginPath(gc);
            CGContextAddPath(gc, shapeInverse);
            CGContextFillPath(gc);
        } CGContextRestoreGState(gc);
    
        CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
        CGContextSetLineWidth(gc, 1);
        CGContextSetLineJoin(gc, kCGLineCapRound);
        CGContextBeginPath(gc);
        CGContextAddPath(gc, shape);
        CGContextStrokePath(gc);
    
        CGPathRelease(shape);
        CGPathRelease(shapeInverse);
    }
    

提交回复
热议问题