Realistic Text Emboss Effect in iOS

In my most recent project, I had to solve the problem of rendering text on a credit card’s background in a realistic, pleasant way.  The client was a bank and cards are a strong part of a bank’s corporate identity.  The text is embossed with complex lighting and shading effects, like this:

Credit-Card

It turns out that iOS has a couple of useful filters that can be used, with the appropriate texture, to obtain just that, namely CIHeightFieldFromMask  and CIShadedMaterial .

Let’s make a single view app, with Storyboard.  I have called mine “Emboss”.  Don’t change any of the default configurations and names that come with the Xcode template for a single view app (as of version 8.1, March 2017).

The problem with filter is the they can only be applied to an image so the first problem we need to solve is how to render a text into an UIImage .  This can be accomplished by the following extension:

Please not that this method takes as an input both your text as a String  and its attributes as a Dictionary .  The last parameter (a CGSize ) represents the rectangle the string needs to be rendered into.  It is worth noting that the returned object is a UIImage  backed by a CGImage .  This is important, not every UIImage  is backed by a CGImage , some are backed by a CIImage .

Now that we have a way to turn a text into images, we can apply the two filters mentioned above, in the correct order and with an arbitrary texture.  Once again we favour an extension to achieve that.

Note that the shading (or texture) image is arbitrary.  The output is a UIImage  backed by a CIImage .  This is important, you cannot extract directly a CGImage  from this output.  If you call myOutputImage.cgImage  the result will be nil.  If you, on the other hand call myOutputImage.ciImage  the result will return the correct CIImage .  If you want to write generic code, this has implications on how functions are chained (we will not discuss or develop further this topic in the current post).

Let’s put it all together.  Add an image view in the main storyboard and pin it to the top and bottom layout guides, left and right margins.  Connect the image view wth an outlet your view controller (call it imageView ).  You should end up with something like this:

Main Storyboard configuration

In order to apply the emboss effect you need a texture and this is where the magic really happens.  Generally speaking you will want a rasterised version of a 3D sphere on a black background. This will represent the material you text will be made of. The position of lights and shades will make your effect as beautiful or as ugly are it deserves. Download the following texture:

Texture

In your Assets.xcassets  make a new image set (call it “texture1”) and add the above texture in the 3x slot.

And finally your ViewController .  Given that converting a string with attributes into text requires a rectangle, we need to trigger the rendering after the views have been laid out.  The key method is viewDidLayoutSubviews .  Here is the full view controller:

As simple as that!  Please note that this code is missing, as stated above, the ability to arbitrarily chain the filter with other operations that require a CGImage , so in that respect the code is a little dirty and would require further work.

You can find the full little mini project here (GitHub).  Did you enjoy? Leave us a nice comment!

CREDITS AND NOTES:

  • The technique shown above is a much simplified version of this repository by FlexMonkey.
  • How did I obtain the texture used in this project?
  • I have made a dirty port of FlexMonkey’s code to swift 3 and modified the code in order be able to produce textures of different colours (including black and white) and to save such textures in a .png  file.
  • Alternatively, if you can, play around with a more sophisticated 3D rendering software and see what happens.  Happy experimenting!

Leave a Reply