Core Graphics - How to draw a shadow without drawing a line?
Asked Answered
P

3

6

Take a look at this code:

- (void)drawRect:(CGRect)rect
{
  [super drawRect:rect];

  CGContextRef context = UIGraphicsGetCurrentContext();

  CGSize  myShadowOffset = CGSizeMake (-10,  15);

  CGContextSaveGState(context);

  CGContextSetShadow (context, myShadowOffset, 5);

  CGContextSetLineWidth(context, 4.0);
  CGContextSetStrokeColorWithColor(context,
                                 [UIColor blueColor].CGColor);
  CGRect rectangle = CGRectMake(60,170,200,200);
  CGContextAddEllipseInRect(context, rectangle);
  CGContextStrokePath(context);
  CGContextRestoreGState(context);
}

what it does it draws a circle and a shadow for this circle. However i can't make my mind how to draw only shadow without drawing circle's line? How do i do that? Answers to similar questions here on SO didn't help me

Parole answered 27/3, 2014 at 13:20 Comment(8)
What happens if you stroke/fill your circle with a completely transparent colour? [[NSColor clearColor] GCColor]Reincarnate
nothing happens. i tried it. just nothingParole
You can try CGContextSetStrokeColorWithColor(context, [UIColor clearColor].CGColor);Agnosticism
@Manixate as i said above it draws nothingParole
Try this may be CGContextSetLineWidth(context, 0.0);Agnosticism
@Manixate it also produces nothing unfortunatelyParole
#8171178 have you tried this?Agnosticism
@Manixate i have. nothing as well. blank screen))Parole
D
5

Please try the following:

//If you would like your shadow color to be blue, change it to blue.
CGContextSetShadowWithColor(context, CGSizeMake(-10.0f,  15.0f), 5.0f, [UIColor lightGrayColor].CGColor);
CGContextSetStrokeColorWithColor(context, self.backgroundColor.CGColor);
CGContextStrokePath(context);

I just ran a test on it; and the effect seems desirable.

Hope this helps.

Dandridge answered 27/3, 2014 at 14:41 Comment(0)
G
12

I was able to achieve this using stroke color white, and blend mode multiply:

// ...
context.setStrokeColor(UIColor.white.cgColor)
context.setShadow(offset: CGSize.zero, blur: 8.0, color: UIColor.black.cgColor)
context.setBlendMode(.multiply)
context.addPath(...) <--- ADD YOUR PATH HERE
context.strokePath()
// ...

It draws only the shadow, without the stroke.

Another approach would be to just draw the line and blur it.

Gerbold answered 12/2, 2019 at 13:53 Comment(2)
That is very clever, been looking for a solution to this for ages.Fey
NOTE only if the background is white !!!!!!!Comeau
D
5

Please try the following:

//If you would like your shadow color to be blue, change it to blue.
CGContextSetShadowWithColor(context, CGSizeMake(-10.0f,  15.0f), 5.0f, [UIColor lightGrayColor].CGColor);
CGContextSetStrokeColorWithColor(context, self.backgroundColor.CGColor);
CGContextStrokePath(context);

I just ran a test on it; and the effect seems desirable.

Hope this helps.

Dandridge answered 27/3, 2014 at 14:41 Comment(0)
E
2

Here is a way to render the shadow only. The way is background color, shadow color agnostic.

The gist is

  1. add a big horizontal offset to the shadow you want to render. So that the shadow is separated out from the shadow shape.
  2. Translate the context with a negative same horizontal offset, so that the shadow shape is moved out of the rendering area, and the shadow is moved back.
  3. Render normally, you will get the shadow rendering only.

Example code:

func normalShadowRendering() {
    let context = UIGraphicsGetCurrentContext()!

    // the shape, a triangle
    let bezierPath = UIBezierPath()
    bezierPath.move(to: CGPoint(x: 0, y: 32))
    bezierPath.addLine(to: CGPoint(x: 16, y: 0))
    bezierPath.addLine(to: CGPoint(x: 32, y: 32))
    bezierPath.close()

    // set shadow
    context.setShadow(offset: CGSize(width: 4, height: 4), blur: 8.0, color: UIColor.black.cgColor)

    // render the shape with shadow
    context.addPath(bezierPath.cgPath)
    UIColor.red.setStroke()
    context.setLineWidth(4)
    context.strokePath()
  }

The rendering result is: enter image description here

func shadowOnlyRendering() {
    let context = UIGraphicsGetCurrentContext()!

    // the shape, a triangle
    let bezierPath = UIBezierPath()
    bezierPath.move(to: CGPoint(x: 0, y: 32))
    bezierPath.addLine(to: CGPoint(x: 16, y: 0))
    bezierPath.addLine(to: CGPoint(x: 32, y: 32))
    bezierPath.close()

    // set shadow
    // `drawingRect` is the canvas rect
    // add the canvas width extra horizontal offset, so that
    // shadow is separated out
    context.setShadow(offset: CGSize(width: 4 + drawingRect.width, height: 4), blur: 8.0, color: UIColor.black.cgColor)

    // compensate the extra horizontal offset added to the shadow
    // so that the shadow is rendered at correct place, and the 
    // main shape is moved out of the canvas.
    context.translateBy(x: -drawingRect.width, y: 0)

    // render the shadow only
    // shape is out of the canvas
    context.addPath(bezierPath.cgPath)
    UIColor.red.setStroke()
    context.setLineWidth(4)
    context.strokePath()
}

The rendering result is: enter image description here

Emera answered 18/12, 2022 at 22:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.