How to draw drop shadows in iOS

CAShapeLayer uses CGPathRef for drawing. Therefore, I have a star path, and I want a smooth shadow with a radius of about 15 units. Some new iPhone versions probably have good functionality, but I need to do this on my own for the old version 3.0 (which most people still use).

I tried to do some REALLY unpleasant things: I created a for-loop and sequentially created, like 15 of these paths, converted them step by step to become bigger. Then assign them to the newly created CAShapeLayer and slightly reduce it at each iteration. Not only is this scaling mathematically incorrect and sucks (this should happen relative to the contour!), The shadow is not rounded and looks really ugly. This is why beautiful soft shadows have a radius.

The tips of the star should not appear absolutely sharp after a shadow size of 15 units. They should be as soft as cream. But in my ugly decision, they are the same as the star itself, because everything I do scales the star 15 times and reduces it by 15 times. Ugly.

I wonder how the big guys do it? If you had an arbitrary path, and this path should cast a shadow, how does the algorithm work like this? Probably, the path should be expanded as 30 times, point-to-point relative to the tangent contour from the filled part and only by 0.5 units, in order to have a pleasant mix.

Before I re-invent the wheel, maybe someone has a handy example or link?

+6
iphone
source share
2 answers

A shadow is a translucent halftone mask of the shape of an object, blurred and offset.

CGContextSetShadowWithColor and CGContextSetShadow as done on the iPhone. You set the shadow, then draw something, and the shadow is applied.

There is no easy way to apply a shadow in CAShapeLayer. You will need to create a custom view or layer and set the shadow before drawing your shape.

I have not tried, but the following may work:

 @interface ShadowShapeLayer : CAShapeLayer @end @implementation ShadowShapeLayer -(void) drawInContext:(CGContextRef)context { CGContextSaveGState( context ); CGContextSetShadow( context , CGSizeMake( 5 , 5 ) , 15 ); [super drawInContext:context]; CGContextRestoreGState( context ); } @end 

Edit: thanks Miser.

+3
source share

I asked myself the same question. I am not an expert on this topic at all, but I had the following thought: physically, one point of the figure should lead to a circular (or elliptical) translucent shadow. Thus, the entire pattern, consisting of several points, should lead to a combination of many such circular shadows.

So, I drew a small shadow in Photoshop (brush, size 7, opacity 33%, color # 3b3b3b). This is almost invisible:

alt text

Then I wrote a little HTML with Javascript to try and see how it looks (definitely not an ideal technique :-):

 <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>Title</title> <script type="text/javascript" language="javascript"> function pageload() { var drawingContainerElem = document.getElementById("drawing-container"); var shadowContainerElem = document.getElementById("shadow-container"); this.drawDot = function(x, y) { var imgElem = document.createElement("img"); imgElem.style.left = x + "px"; imgElem.style.top = y + "px"; imgElem.src = "blue-dot.png"; drawingContainerElem.appendChild(imgElem); } this.drawShadow = function(x, y) { var imgElem = document.createElement("img"); imgElem.style.left = x + "px"; imgElem.style.top = y + "px"; imgElem.src = "soft-shadow.png"; shadowContainerElem.appendChild(imgElem); } this.drawDotAndShadow = function(x, y) { drawShadow(x - 5, y - 1); drawDot(x, y); } for (var x = 50; x < 70; x ++) { for (var y = 50; y < 58; y ++) { drawDotAndShadow(x, y); } } for (var x = 0; x < 15; x ++) { for (var y = 0; y < x; y ++) { drawDotAndShadow(69 + 15 - x, 54 + y); drawDotAndShadow(69 + 15 - x, 54 - y); } } } </script> <style type="text/css"> #drawing-container { position: absolute; left: 2em; top: 2em; width: 400px; height: 400px; z-index: 2; } #shadow-container { position: absolute; left: 2em; top: 2em; width: 400px; height: 400px; z-index: 1; } #drawing-container img { position: absolute; } #shadow-container img { position: absolute; } </style> </head> <body onload="javascript:pageload()"> <div id="drawing-container"></div> <div id="shadow-container"></div> </body> </html> 

This is the result:

alt text

There are probably many opportunities for optimization, and, of course, you would not do it with javascript this way ... Maybe you can find a way to render efficiently on the iPhone? If so, let me know!

Possible improvements:

  • Make the center of the shadow circle darker (more opacity) and the rest lighter (less opacity) to achieve the main shadow.
  • Scale the shadow: make it a little smaller to achieve the effect of depth.
+1
source share

All Articles