Our competences at your disposal on our blog
We love our job and we are glad to share it with you! Keep yourself fully informed about news and technologies we use to develop and which you may need! Follow our blog.
alex.reggiani's picture

During the development of Mobile Applications, it emerges the need of creating different applications which share the same features but using different resources. With the term “resource” I mean graphic assets, texts, and in general the application style.
Unfortunately iOS doesn’t provide different themes for our applications, because its language does not support any mechanism equivalent to CSS. For this reason we need to think about different alternatives and bypass the problem to succeed in this intent.
There are various approaches that we can use, but they depend on our particular needs and on how the application is structured.

First of all, we need to choose which application features we want to make themeable.

Suppose you want to change colors and images, but in order to do this, you have to launch again the App ( this makes things easier).

You have to create a plist file containing all the graphic resources that you want to use for the theme.
The plist is a dictionary that has all the references to images, font and colors used. Referring to the images, we can use an identification name as a key, to describe what the image is representing (ex: "defaultBackgroundImage"), and the file name as value.
Instead, for colors, we try to use keys that have names as general as possible (ex: we don’t name them as “blueColor” or “yellowColor” but as “primaryColor” or “secondaryColor”) and, as value, we emply the string that represent the set of three components such as RGB 0,255,0. or RGBA 0,255,0,1 ( the alpha component must be included between 0 and 1).

We have to create a plist for every application’s theme.
We build a new class of Singleton type named ThemeManager, by adding the following method:

+ (ThemeManager *)sharedInstance
{
	static ThemeManager *sharedInstance = nil;
	if (sharedInstance == nil)
	{
   	sharedInstance = [[ThemeManager alloc] init];
	}
	return sharedInstance;
}

We define a property of type NSDictionary called Theme into the class ThemeManager, then we upload into the init method the theme saved into the plist file.

- (id)init
{
	if ((self = [super init]))
	{
       NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   	NSString *themeName = [defaults objectForKey:@"theme"] ?: @"default";
       
   	NSString *path = [[NSBundle mainBundle] pathForResource:themeName ofType:@"plist"];
       self.theme = [NSDictionary dictionaryWithContentsOfFile:path];
	}
   return self;

}

In this way, the Theme is read by the user personal setting, in case the user hasn’t selected any theme, it will read the default one. We must be sure we have create a default.plist file in our project, that will have the application base theme.
Now that we have uploaded the theme, we are going to use it and there are two possible scenarios:
1)making the App themeable by using a code
2)making the App themeable by using xib

In this post i am going to discuss the first case, while i postpone to the next time the explanation of the various solutions for making the application themeable by using xib files.

In order to upload an image, normally you should use this instruction:

UIImage *image = [UIImage imageNamed:@"defaultBackgroundImage.png"];

ma se vogliamo renderla temabile, allora dobbiamo utilizzare l'oggetto ThemeManager appena creato, e lo facciamo in questo modo

NSDictionary *theme = [ThemeManager sharedInstance].theme;
NSString *imageName = [theme objectForKey:@"defaultBackgroundImageKey"];
UIImage *image = [UIImage imageNamed:imageName];

Now, search, into the uploaded theme, for the actual name of the image set to the theme that is the key @ "defaultBackgroundImageKey" and depending on the theme loaded you will get a different picture.
If we have many images in the application, then we have to use the block of code above. It is convenient to create a category on UIImage class, so that it can respond to such method

+ (UIImage *)themeImageNamed:(NSString *)imageKey

At this point, if we want to thematize the image, just use the instruction:
UIImage *image = [UIImage themeImageNamed:@"defaultBackgroundImageKey"]
As for the colours, we normally assign a color to a label, following this method:

myLabel.textColor = [UIColor blueColor];

But if we want to make it themeable, then we use our ThemeManager in this way:

NSDictionary *theme = [ThemeManager sharedInstance].theme;
NSString *labelColor = [theme objectForKey:@"myLabelColor"];
myLabel.textColor = [UIColor colorWithRGBAString:labelColor];

The same thing matters even for colours, it is more convenient to incorporate this block of code in a category in order to reduce the allocation as follows:

+ (UIColor *)themeColorNamed:(NSString *)colorKey

At this point, in order to make a label colour themeable, it is enough to use the newly created category in this way:

myLabel.textColor = [UIColor themeColorNamed:@"primaryColor"];

Note:

The UIColor class hasn’t a method called colorWithRGBAString: by default, but it can be attached in a category and then in a possible implementation.

+ (UIColor *)colorWithRGBAString:(NSString *)RGBAString {
	UIColor *color = nil;
   
	NSArray *rgbaComponents = [RGBAString componentsSeparatedByString:@","];
	float RED = 0.0f;
	float GREEN = 0.0f;
	float BLUE = 0.0f;
	float ALPHA = 0.0f;
	if ([rgbaComponents count] == 3) {
   	RED = [(NSString*)[rgbaComponents objectAtIndex:0] floatValue]/255;
   	GREEN = [(NSString*)[rgbaComponents objectAtIndex:1] floatValue]/255;
   	BLUE = [(NSString*)[rgbaComponents objectAtIndex:2] floatValue]/255;
       
   	color = [UIColor colorWithRed:RED green:GREEN blue:BLUE alpha:1.0f];
	}else if ([rgbaComponents count] == 4) {
   	RED = [(NSString*)[rgbaComponents objectAtIndex:0] floatValue]/255;
   	GREEN = [(NSString*)[rgbaComponents objectAtIndex:1] floatValue]/255;
   	BLUE = [(NSString*)[rgbaComponents objectAtIndex:2] floatValue]/255;
   	ALPHA = [(NSString*)[rgbaComponents objectAtIndex:3] floatValue]/255;
       
   	color = [UIColor colorWithRed:RED green:GREEN blue:BLUE alpha:ALPHA];
	}
   
	return color;
}

Eventually, we examine the font.
The font assignment allows two differents parameters, the name and the dimension:

myLabel.font = [UIFont fontWithName:@"Helvetica" size:18.0];

In order to make it themeable by using ThemeManager we should read not one, but two parameters:

NSDictionary *theme = [ThemeManagersharedInstance].theme;
NSString *fontName = [theme objectForKey:@"primaryFontName"];
NSNumber *fontSize = [theme objectForKey:@"primaryFontSize"];
myLabel.font = [UIFont fontWithName:fontName
                              size:[fontSize doubleValue]];

As well as for colours and images, we can simplify this procedure by using a category:

+ (UIFont *)themeFontNamed:(NSString *)fontName ofSize:(NSNumber *)fontSize

The font assignment to our label will be like:

myLabel.font = [UIFont themeFontNamed:fontName ofSize:fontSize];

In the next post I'll show you what are the possible ways for making your application themeable directly by using xib files.

iOS

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Type the characters you see in this picture. (verify using audio)

Type the characters you see in the picture above; if you can't read them, submit the form and a new image will be generated. Not case sensitive.