The last post was about drawing inner shadows on the inside of a known shape to give the effect that the shape was embossed, or indented. The approach taken was quite a lot of work, and only works for known shapes; we couldn’t for instance indent an image or letters with this approach. We also need to write new code for any shape that we wish to indent. It would be nice to have an object that would do all of this work for us for any given shape.

To automatically create inner shadows we will in some form have to do our own drawing. We can do this by making a subclass of CALayer. The way drawing works on a CALayer is you call setNeedsDisplay on your layer, this adds an item to the runloop to call display on your layer the next time round the run loop. Any subsequent calls to setNeedsDisplay won’t do anything until display has been called by the runloop. display works by calling your drawing code and caching the result in the layers’ content, then display won’t be called again unless the setNeedsDisplay method is called. To do custom drawing we can do all of this ourselves with our own customisations.

The first thing that we are going to do in our display method is to create a CGContextRef we can then pass this to either the delegates drawLayer:inContext:, or the layers’ drawInContext: method which will do the normal drawing. Here’s the code:

To make this work for a retina device you will need twice as many pixels in each direction and to set a transform to make the drawing happen at the right size.

Next we need to call the appropriate drawing method:

If at this point we called setContents:mask then we would have effectively re-implemented the original display method. Probably less efficiently, with some memory leaks!

We can now manipulate what has been drawn to create the shadows that we to draw. To draw the outside shadow we will draw mask with the context set to draw the correct shadow. To draw the inside shadow we invert the alpha channel of image data, clip to mask, set up the shadows that we want, and draw the inverted alpha image.

First we will create the image with the inverted alpha channel. We have all the bitmap data of the drawn image in dataBytes, so we can fiddle with this directly.

Next we are going to draw the outside shadow. If we want to make it so that the original image isn’t drawn then we can clip to the inverted image before we draw the original data (which is in mask).

Next clip to the original image and draw the inverted image with a shadow, this will make the inside shadow.

Finally clear up, and set the result so that it is drawn to screen.

Now for any view that does its drawing using the drawRect: method all we need to do to make inner shadows is to subclass the view, and swap its layer for our own. With what we’ve done if the view does its drawing in a different way, this layer won’t help us.

Here’s an example of a UILabel subclass changed to indent its content:

That’s it! here’s the result:

By changing the colour and size of the shadows we can emboss things instead of indenting them; resulting in something like this:

We can also make our own image view like this:

Here’s what the results look like:

With this CGLayer subclass we can quickly and very easily indent a lot of things. There are some possible issues with this. We use quite a lot of memory to manipulate the images, and we have to loop over every pixel in the image to invert the alpha channel. This may cause issues if you are doing this a lot, or if you have a very large drawing area.

You can find and use the code for this layer here. If you use this code please give me some credit for it.

#### Update

I’ve added a delegate method to the CALayer delegate protocol that allows you to draw whatever you want in the embossed, or indented region. The method is called drawToshadowedRegionInContext:. Here’s an example of using this method (this code is added to your UIView subclass):

I added this to my emboss view class, and this is the result:

I’ve committed these changes to bitbucket, and the source is still
here