Manageable theming in iOS apps

Theming is the concept of a consistent tone of look and feel across the app. This blog discusses an approach to do manageable theming across the app. 

Lets say the app has a dark theme, wherein the UI elements like view background, label text color, navigation bar color etc share the dark tone. The simplest way is to apply the color or relevant images as and when the UI elements are created. But throw in couple of good design patterns to this simple recipe. 

To start with, the theme itself is a protocol 

// Theme.h
@protocol Theme
@required
// Theming UITableView
- (void)themeTableView:(UITableView*)tableView;
- (void)themeTableViewCell:(UITableViewCell*)cell;
- (void)themeTableCellTitle:(UILabel*)titleLabel;
- (void)themeTableCellSubTitle:(UILabel*)subTitleLabel;
// Theming general UI Controls
- (void)themeTitleLabel:(UILabel*)label;
- (void)themeProfileImage:(UIImageView*)imageView;
- (void)themeViewBackground:(UIView*)view;
@end

Every theme has to adhere to Theme protocol. Lets say we have DarkTheme and LightTheme as two different theme implementors. 

An example implementation of DarkTheme would look like

// DarkTheme.h
@interface DarkTheme : NSObject <Theme>
@end

// DarkTheme.m
@implementation DarkTheme
- (void)themeTableView:(UITableView*)tableView {
    tableView.backgroundColor = [UIColor colorWithWhite:0.249 alpha:1.000];
    tableView.separatorColor = [UIColor colorWithWhite:0.664 alpha:1.000];
}
// Implementation of other methods ...
@end

Now we introduce another class called ThemeProvider. As the name suggests, this class provides the theme object to be used across the app. 

Here we are doing a Dependency Injection so that the themes (Dark, Light etc) are not hardcoded, but fetched at runtime, without ever knowing who is implementing it. All we are concerned is that theme object adheres to @protocol Theme.

// ThemeProvider.h
#import "Theme.h"
@interface ThemeProvider : NSObject
+ (id<Theme>)theme; 
@end

// ThemeProvider.m
static NSMutableDictionary* _themes;
@implementation ThemeProvider 
+ (id<Theme>)theme {
    NSString* themeName = [[NSUserDefaults standardUserDefaults] 
                                        valueForKey:kAppTheme];
    id<Theme> theme = [_themes objectForKey:themeName];
    if(!_themes)
        _themes = [[NSMutableDictionary alloc] init];
    if(!theme) {
        if(themeName == @"dark") {
                theme = [[DarkTheme alloc] init];
        } else if(themeName == @"light") {
              // create light theme
        } else {
             // create default theme
       }
        [_themes setValue:theme forKey:themeName];
    }
    return theme;
   }
@end

After creating an UI component, theme it by sending the relevant message to ThemeProvider

- (void)tableView:(UITableView *)tableView 
       willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
        [[ThemeProvider theme] themeTableViewCell:cell];
}

Take a closer look at NSUserDefaults from which we fetch the app theme. Set the app theme at app launch time.

When developers closely work with designers, frequent UI changes are natural. Using this approach you can modify theme class – and all UI Components using this theme reflects it. But dynamic theming (changing theme at runtime) is a topic for itself. It is not a good user experience to change the entire theme when user is interacting with the app. A better approach would be to (re)apply the theme when the app is relaunched (Say, changing to red-white theme on christmas eve!). For dynamic theming to work, you should maintain discipline in code by maintaining all theming messages in a single method, and invoking it when theme changes. You can do this by posting a theme change notification.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s