Rendering Tinted Icons Using SVG Files in Titanium

Icon designed by Simpleicon Business from flaticon.com

This tutorial shows how to render a tinted (or colourised) SVG icon file in Appcelerator/Axway Titanium using 2 different techniques: a cross-platform method that renders a bitmap image from an SVG file and tints it and an Android only method that tints an SVG file by dynamically editing the file before rendering. It assumes that you have basic knowledge of how to use Titanium, Alloy and native modules.

The example project was developed using Ti SDK 6.2.2 on OS X 10.12.6 (with XCode 8.3.3) and tested on iOS 10.3 and Android 4 and 6. It also uses the cross-platform module Ti.SVGView (see below for more details).

The default technique for rendering a resolution-independent icon — tinted or otherwise — that many people choose is to use a custom icon font and render the icon as text in a Ti.UI.Label (see eg http://www.tidev.io/2014/03/25/create-an-icon-font-with-icomoon/). Fonts are vector definitions of shapes and so are by definition resolution independent. Resolution independence in mobile apps, using any technique, is a huge boon these days as it means that we don’t have to bundle many different versions of a bitmap image asset with our app. These include different density versions @1x, @2x, @3x etc for different density screens and different versions for iOS and Android (mainly because of file naming convention differences). That’s great, but I find that when rendering them in Titanium within a Label I don’t have the pixel accuracy I need to size and position them accurately. This is because Titanium does not in general give you access to all the font metrics of a font, and even if it did it is difficult to know exactly where the baseline and ascenders/descenders of a font (or parts of the icon) go. If you specify the size of the icon using the fontSize property in points/pixels you don’t know exactly what the dimensions of the graphic will be when rendered. You also have to use quite a lot of TSS styling and properties to define the icon. In other words, for a designer or UX specialist (which I am), it’s irritating.

A better way I have found is to use SVG files. Most icon libraries can export their icons as SVG files. I like flaticon.com. Again, like fonts, they are vector based and are resolution independent. We can map individual icons to different individual SVG files. With fonts, each time we want to add or change a font in our app, we have to add it to the font library we are using, save the library and replace the old one in our app project. Also, I think it is easier to keep track of what files/icons we are using with a flat file structure. SVG files are simple text files and so it also gives us the ability to edit them either pre-compile or dynamically in-app. I will show a method for doing this below.

So how do we do render an SVG file? I am using the very handy cross-platform SVG rendering module Ti.SVGView from Geraud Bourdin, and the branched Android version from Andrey Tkachenko (falkolab). They basically expose standard native SVG libraries. Basic instructions for installing and using these modules is on their GitHub pages and so I will not go over that here. The only problem is that the iOS module hasn’t been updated for over 2 years and so is out-of-date (it will render to the Simulator and devices up to about iPhone 6). It needs to be updated for iOS to use the latest SVGKit and the module recompiled for Titanium. Also, the iOS version only takes relative file paths and would be much more useful if it supported absolute file paths. As I don’t have the required native iOS experience (I have updated the Android version to work with Ti SDK 6) anyone who fancies volunteering to do these updates are most welcome and please do let me know!

Method 1: Render a tinted bitmap from an SVG file

Using Ti.UI.ImageView we can assign an image blob created by the SVG module in the code below. “calendar_icon.svg” is an SVG file.

index.xml:
<Alloy>
<Window id="win">
<ImageView id="icon"/>
</Window>
</Alloy>
index.js:
var SVGView = require('com.geraudbourdin.svgview');
var width = height = 100,
imageScaleFactor = Ti.Platform.displayCaps.logicalDensityFactor,
filePath = '/images/svg/calendar_icon.svg';
var svgOptions = {
image: filePath,
width: width * imageScaleFactor,
height: height * imageScaleFactor
};
$.icon.width = width;
$.icon.height = height;
$.icon.image = SVGView.createView(svgOptions).toImage();

Note that we scale the image width and height passed to the SVGView module by the device logicalDensityFactor. This renders our @1x/@2x/@3x etc version of the bitmap we need — and only at the necessary required resolution (we could even, if we wanted to save memory, render an image at less than the device resolution).

We then have to tint the image. For simplicity, we are just going to talk first about single colour images — which icons often are. The same techniques work for multi-coloured icons or graphics but the results are more complicated. Later on we will see how we can do multi-coloured tinting. Since Ti SDK 5.4 there has been support for tinting images using the “tintColor” property. So for example to tint the image red we would do:

index.js:
$.icon.tintColor = ‘#FF0000’;

We have to be careful here as tinting works differently on iOS and Android. On iOS tinting overwrites the image with the tint colour. On Android it multiplies the colour with the tint, so if the underlying colour of the icon is black (r = g = b = 0) the tint will not work (0 x anything = 0!). So for Android we have to use an SVG icon which is coloured white, to get the full tint colour.

If we want to do more complex tinting it is possible to use Ti.UI.createMaskedImage on iOS with a tint option (although note that the resulting image is flipped vertically so we have to flip it back with Ti.UI.create2DMatrix().scale(1, -1)). As the ink mode is also definable with this call it gives us more variety in how the final image is generated. There is a custom module by Michael Gangolf for Android https://github.com/m1ga/tintimage that I tried which replicates this function, although I found it leaks memory and so wasn’t useable.

Now its time to talk about the file structure of SVG files and another technique we can use for tinting and much more.

Method 2: Render a dynamically source-edited SVG file

SVG files are basically XML-format text files. The origin in the SVG coordinate system is at the top left corner of the viewport at point (0, 0). This is not a tutorial on SVG files, but a simple example might be:

triangle.svg:
<?xml version="1.0" encoding="UTF-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width=“150” height="200">
<path fill=“#FF0000” d="M75 0 L0 200 L150 200 Z" />
</svg>

This describes a red triangle shape using the “path” tag. The path starts at position (75, 0); with a line to position (0, 200); then from there, a line to (150, 200); and finally closing the path back to (75, 0). It produces this shape:

Image rendered from triangle.svg

The files I have used tend to be much more complicated than this. They are often created in, or come as, Adobe Illustrator exported files and I frequently have to edit them to change the icon design and simplify them (again in Adobe Illustrator, or maybe Sketch, or by hand editing the SVG file text). But essentially the basic idea is the same.

Now knowing something of the structure of our SVG file we can read the text file into memory and do things like search for the colour markup code and swap the text of the colour value for a different one. If we want to be even more sophisticated we can open the file and access the XML file structure using Ti.XML.parseString and use the Titanium XML DOM (I haven’t got around to doing that yet), but lets keep it simple for now:

var filePath = '/images/svg/triangle.svg';
var tint = ‘#00FF00’;
var file = Ti.Filesystem.getFile(filePath);
var blob = file.read();
var source = blob.text;
source = source.replace(/#FF0000/ig, tint);
blob = Ti.createBuffer({value: source}).toBlob();

This replaces the red colour string (#FF0000) with one for green (#00FF00) and creates a new blob. We can then save the blob out to a new SVG file. We can’t overwrite or add to a file in the “images” folder as the Resources directory is read-only, so we have to create a file in the applicationDataDirectory directory and write to that.

file = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, ‘green_triangle.svg’);
file.write(blob);

Being able to edit and save out an SVG file like this actually gives us endless possibilities for versioning and editing the original file. The only problem with this is that Geraud’s iOS module only supports relative paths and access to the Resources directory. The Android module does support absolute paths.

In the final project I use a calendar icon from FlatIcon which looks like this (see the project source code to review the much more complex SVG file with lots of vertices!):

Render of calendar_icon.svg with a red tint

So finally here is the image library code that encompasses all these things:

You can see the full completed project here on GitHub with examples of the render SVG and tint method and the edit source SVG text and render method.

Code strong!

Simon Buckingham is a designer, UX specialist, animator, creative and technical director and developer with more than 20 years experience. He has been making cross-platform iOS/Android mobile apps with Titanium for more than 5 years. Simon runs his own company Icecandy Entertainment, based in London, UK. You can find out more about his digital career at simonbuckingham.me.