Skip to content

Instantly share code, notes, and snippets.

@leeprobert
Last active December 16, 2015 13:29
Show Gist options
  • Save leeprobert/5442137 to your computer and use it in GitHub Desktop.
Save leeprobert/5442137 to your computer and use it in GitHub Desktop.
A collection of classes and a plist file that can be used to create ios themes with UIAppearance proxy. Note that the styles.plist contains all of the valid CSS color name references. You can use hex values as well as common ios color methods like 'redColor'. Will be adding the ability to also add RGB values as strings. Also note the string shor…
//
// StyleManager.h
// Engager
//
// Created by Lee Probert on 22/04/2013.
//
//
// PLIST FILENAME KEY IN INFO PLIST
#define kStylesFilenameInfoKey @"**.**.*****"
#import <Foundation/Foundation.h>
@interface StyleManager : NSObject
@property (nonatomic, strong) NSString* filename;
@property (nonatomic, strong) NSDictionary* styleData;
@property (nonatomic, strong) NSDictionary* colorsData;
@property (nonatomic, strong) NSDictionary* fontsData;
- (id)initWithStyles:(NSString*)filename;
@end
//
// StyleManager.m
// Engager
//
// Created by Lee Probert on 22/04/2013.
//
// https://****************/browse/DEV-355
#import "StyleManager.h"
#import "UIColor+Hex.h"
@interface StyleManager ()
-(void) loadStyles;
-(NSDictionary*) getTitleTextAttributesForComponent:(Class)component forState:(UIControlState)controlState;
-(UIColor*) getColorForStyleKey:(NSString*)key;
-(UIFont*) getFontForStyleKey:(NSString*)key;
-(NSValue*) getShadowOffsetForComponent:(Class)component forState:(UIControlState)controlState;
-(CGFloat) getMetricsFloatValueForKey:(NSString*)key;
@end
@implementation StyleManager
- (id)init
{
self = [super init];
if (self) {
/*
Get the required style plist from the bundle. The filename is set in the
*/
NSBundle *bundle = [NSBundle mainBundle];
NSDictionary *info = [bundle infoDictionary];
self.filename = (NSString*)info[kStylesFilenameInfoKey];
[self loadStyles];
}
return self;
}
- (id)initWithStyles:(NSString*)filename
{
self = [super init];
if (self) {
self.filename = filename;
if(nil != filename){
[self loadStyles];
}
}
return self;
}
#pragma mark - Setters
-(void) setFilename:(NSString *)filename {
_filename = filename;
[self loadStyles];
}
#pragma mark - Getters
-(NSDictionary*) colorsData {
if(nil == _colorsData){
_colorsData = (NSDictionary*)self.styleData[@"colors"];
}
return _colorsData;
}
-(NSDictionary*) fontsData {
if(nil == _fontsData){
_fontsData = (NSDictionary*)self.styleData[@"fonts"];
}
return _fontsData;
}
-(NSDictionary*) metricsData {
if(nil == _metricsData){
_metricsData = (NSDictionary*)self.styleData[@"metrics"];
}
return _metricsData;
}
#pragma mark - Private
-(void)loadStyles {
/*
Now load the styles plist
*/
NSString * plistPath = [[NSBundle mainBundle] pathForResource:self.filename ofType:@"plist"];
self.styleData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
/*
Global component appearance proxy
*/
if([UINavigationBar respondsToSelector:@selector(appearance)])
{
[[UINavigationBar appearance] setTitleTextAttributes: [self getTitleTextAttributesForComponent:[UINavigationBar class] forState:0]];
[[UINavigationBar appearance] setTintColor:[UIColor darkGrayColor]];
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:[self getMetricsFloatValueForKey:@"navigationBarTitleVerticalPositionAdjustment"] forBarMetrics:UIBarMetricsDefault];
}
if([UIBarButtonItem respondsToSelector:@selector(appearance)])
{
[[UIBarButtonItem appearance] setTitleTextAttributes: [self getTitleTextAttributesForComponent:[UIBarButtonItem class] forState:0] forState:UIControlStateNormal];
}
if([UIProgressView respondsToSelector:@selector(appearance)])
{
[[UIProgressView appearance] setProgressTintColor: [self getColorForStyleKey:@"primaryBrandColor"]];
[[UIProgressView appearance] setTrackTintColor:[self getColorForStyleKey:@"lightGrayColor"]];
}
if([UISwitch respondsToSelector:@selector(appearance)])
{
[[UISwitch appearance] setOnTintColor:[self getColorForStyleKey:@"darkGrayColor"]];
}
if([UISegmentedControl respondsToSelector:@selector(appearance)])
{
[[UISegmentedControl appearance] setTitleTextAttributes:[self getTitleTextAttributesForComponent:[UISegmentedControl class] forState:UIControlStateNormal] forState:UIControlStateNormal];
[[UISegmentedControl appearance] setTitleTextAttributes:[self getTitleTextAttributesForComponent:[UISegmentedControl class] forState:UIControlStateSelected] forState:UIControlStateSelected];
}
}
-(NSDictionary*) getTitleTextAttributesForComponent:(Class)component forState:(UIControlState)controlState {
NSDictionary *attrib;
if([UINavigationBar class] == component) {
attrib = @{
UITextAttributeTextColor: [self getColorForStyleKey:@"primaryBrandColor"],
UITextAttributeTextShadowColor: [self getColorForStyleKey:@"clearColor"],
UITextAttributeTextShadowOffset: [self getShadowOffsetForComponent:component forState:0],
UITextAttributeFont: [self getFontForStyleKey:@"mainNavBarFont"],
};
} else if ([UIBarButtonItem class] == component) {
attrib = @{
UITextAttributeTextColor: [self getColorForStyleKey:@"black"],
UITextAttributeTextShadowColor: [self getColorForStyleKey:@"clearColor"],
UITextAttributeTextShadowOffset: [self getShadowOffsetForComponent:component forState:0],
UITextAttributeFont: FONT_AVENIR_12,
};
}else if ([UISegmentedControl class] == component) {
if(controlState == UIControlStateNormal){
attrib = @{
UITextAttributeTextColor: [self getColorForStyleKey:@"white"],
UITextAttributeTextShadowColor: [self getColorForStyleKey:@"clearColor"],
UITextAttributeTextShadowOffset: [self getShadowOffsetForComponent:component forState:UIControlStateNormal],
};
}else if(controlState == UIControlStateSelected){
attrib = @{
UITextAttributeTextColor: [self getColorForStyleKey:@"primaryBrandColor"],
UITextAttributeTextShadowColor: [self getColorForStyleKey:@"clearColor"],
UITextAttributeTextShadowOffset: [self getShadowOffsetForComponent:component forState:UIControlStateSelected],
};
}
}
return attrib;
}
-(CGFloat) getMetricsFloatValueForKey:(NSString*)key {
NSNumber *n = (NSNumber*)(self.metricsData[key]);
return [n floatValue];
}
-(NSValue*) getShadowOffsetForComponent:(Class)component forState:(UIControlState)controlState {
NSValue* value;
NSArray *offsetSrc;
CGFloat h;
CGFloat v;
if([UINavigationBar class] == component) {
offsetSrc = [(NSString*)self.metricsData[@"navigationBarTitleShadowOffset"] componentsSeparatedByString:@","];
} else if ([UIBarButtonItem class] == component) {
offsetSrc = [(NSString*)self.metricsData[@"barButtonItemTextShadowOffset"] componentsSeparatedByString:@","];
}else if ([UISegmentedControl class] == component) {
if(controlState == UIControlStateNormal){
offsetSrc = [(NSString*)self.metricsData[@"segmentedControlNormalTextShadowOffset"] componentsSeparatedByString:@","];
}else if(controlState == UIControlStateSelected){
offsetSrc = [(NSString*)self.metricsData[@"segmentedControlSelectedTextShadowOffset"] componentsSeparatedByString:@","];
}
}
h = offsetSrc? [(NSString*)offsetSrc[0] floatValue] : 1.0f;
v = offsetSrc? [(NSString*)offsetSrc[1] floatValue] : 1.0f;
value = [NSValue valueWithUIOffset:UIOffsetMake(h, v)];
return value;
}
-(UIColor*) getColorForStyleKey:(NSString*)key {
NSString *value = (NSString*)self.colorsData[key];
/*
If value is nil can we resolve it by capitalization to a CSS name value?
*/
if(nil == value){
value = (NSString*)self.colorsData[[key capitalizedString]];
if(nil == value){
return [UIColor blackColor];
}
}
/*
Can UIColor resolve the value as a class method?
*/
SEL selector = NSSelectorFromString(value);
BOOL isColorMethod = [[UIColor class] respondsToSelector:selector];
BOOL isHex = [value hasPrefix:@"#"];
// If not HEX or a UIColorMethod, can we resolve the value to a CSS name?
BOOL isCSSName = nil != self.colorsData[value];
if(isCSSName){
value = (NSString*)self.colorsData[value];
isHex = YES;
}
// Is this an RGBA color string?
BOOL isRGBA = [[value componentsSeparatedByString:@","] count]==4;
if(isRGBA){
NSArray *rgba = [value componentsSeparatedByString:@","];
CGFloat r = [(NSString*)rgba[0] floatValue];
CGFloat g = [(NSString*)rgba[1] floatValue];
CGFloat b = [(NSString*)rgba[2] floatValue];
CGFloat a = [(NSString*)rgba[3] floatValue];
UIColor *rgbaCol = [UIColor colorWithRed:r green:g blue:b alpha:a];
return rgbaCol;
}
UIColor *color;
SuppressPerformSelectorLeakWarning(
color = isColorMethod? [[UIColor class] performSelector:selector] :
isHex? [UIColor colorWithHexString:value] : [UIColor blackColor];
);
return color;
}
-(UIFont*) getFontForStyleKey:(NSString*)key {
NSString *fontConfigString = (NSString*)self.fontsData[key];
NSArray *properties = [fontConfigString componentsSeparatedByString:@":"];
NSString *fontName = properties[0];
NSString *fontSize = properties[1];
UIFont *font = [UIFont fontWithName:fontName size:[fontSize floatValue]];
return font;
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>colors</key>
<dict>
<!-- RGBA Example -->
<key>RGBA_example</key>
<string>0.1:0.55:0.65:1</string>
<!-- Custom Theme Colors -->
<key>mainBackgroundColor</key>
<string>#333333</string>
<key>primaryBrandColor</key>
<string>#fae04e</string>
<!-- CSS colors names -->
<key>AliceBlue</key>
<string>#F0F8FF</string>
<key>AntiqueWhite</key>
<string>#FAEBD7</string>
<key>Aqua</key>
<string>#00FFFF</string>
<key>Aquamarine</key>
<string>#7FFFD4</string>
<key>Azure</key>
<string>#F0FFFF</string>
<key>Bisque</key>
<string>#FFE4C4</string>
<key>Black</key>
<string>#000000</string>
<key>BlanchedAlmond</key>
<string>#FFEBCD</string>
<key>Blue</key>
<string>#0000FF</string>
<key>BlueViolet</key>
<string>#8A2BE2</string>
<key>Brown</key>
<string>#A52A2A</string>
<key>BurlyWood</key>
<string>#DEB887</string>
<key>CadetBlue</key>
<string>#5F9EA0</string>
<key>Chartreuse</key>
<string>#7FFF00</string>
<key>Chocolate</key>
<string>#D2691E</string>
<key>Coral</key>
<string>#FF7F50</string>
<key>CornflowerBlue</key>
<string>#6495ED</string>
<key>Cornsilk</key>
<string>#FFF8DC</string>
<key>Crimson</key>
<string>#DC143C</string>
<key>Cyan</key>
<string>#00FFFF</string>
<key>DarkBlue</key>
<string>#00008B</string>
<key>DarkCyan</key>
<string>#008B8B</string>
<key>DarkGoldenRod</key>
<string>#B8860B</string>
<key>DarkGray</key>
<string>#A9A9A9</string>
<key>DarkGreen</key>
<string>#006400</string>
<key>DarkKhaki</key>
<string>#BDB76B</string>
<key>DarkMagenta</key>
<string>#8B008B</string>
<key>DarkOliveGreen</key>
<string>#556B2F</string>
<key>DarkOrange</key>
<string>#FF8C00</string>
<key>DarkOrchid</key>
<string>#9932CC</string>
<key>DarkRed</key>
<string>#8B0000</string>
<key>DarkSalmon</key>
<string>#E9967A</string>
<key>DarkSeaGreen</key>
<string>#8FBC8F</string>
<key>DarkSlateBlue</key>
<string>#483D8B</string>
<key>DarkSlateGray</key>
<string>#2F4F4F</string>
<key>DarkTurquoise</key>
<string>#00CED1</string>
<key>DarkViolet</key>
<string>#9400D3</string>
<key>DeepPink</key>
<string>#FF1493</string>
<key>DeepSkyBlue</key>
<string>#00BFFF</string>
<key>DimGray</key>
<string>#696969</string>
<key>DodgerBlue</key>
<string>#1E90FF</string>
<key>FireBrick</key>
<string>#B22222</string>
<key>FloralWhite</key>
<string>#FFFAF0</string>
<key>ForestGreen</key>
<string>#228B22</string>
<key>Fuchsia</key>
<string>#FF00FF</string>
<key>Gainsboro</key>
<string>#DCDCDC</string>
<key>GhostWhite</key>
<string>#F8F8FF</string>
<key>Gold</key>
<string>#FFD700</string>
<key>GoldenRod</key>
<string>#DAA520</string>
<key>Gray</key>
<string>#808080</string>
<key>Green</key>
<string>#008000</string>
<key>GreenYellow</key>
<string>#ADFF2F</string>
<key>HoneyDew</key>
<string>#F0FFF0</string>
<key>HotPink</key>
<string>#FF69B4</string>
<key>IndianRed</key>
<string>#CD5C5C</string>
<key>Indigo</key>
<string>#4B0082</string>
<key>Ivory</key>
<string>#FFFFF0</string>
<key>Khaki</key>
<string>#F0E68C</string>
<key>Lavender</key>
<string>#E6E6FA</string>
<key>LavenderBlush</key>
<string>#FFF0F5</string>
<key>LawnGreen</key>
<string>#7CFC00</string>
<key>LemonChiffon</key>
<string>#FFFACD</string>
<key>LightBlue</key>
<string>#ADD8E6</string>
<key>LightCoral</key>
<string>#F08080</string>
<key>LightCyan</key>
<string>#E0FFFF</string>
<key>LightGoldenRodYellow</key>
<string>#FAFAD2</string>
<key>LightGray</key>
<string>#D3D3D3</string>
<key>LightGreen</key>
<string>#90EE90</string>
<key>LightPink</key>
<string>#FFB6C1</string>
<key>LightSalmon</key>
<string>#FFA07A</string>
<key>LightSeaGreen</key>
<string>#20B2AA</string>
<key>LightSkyBlue</key>
<string>#87CEFA</string>
<key>LightSlateGray</key>
<string>#778899</string>
<key>LightSteelBlue</key>
<string>#B0C4DE</string>
<key>LightYellow</key>
<string>#FFFFE0</string>
<key>Lime</key>
<string>#00FF00</string>
<key>LimeGreen</key>
<string>#32CD32</string>
<key>Linen</key>
<string>#FAF0E6</string>
<key>Magenta</key>
<string>#FF00FF</string>
<key>Maroon</key>
<string>#800000</string>
<key>MediumAquaMarine</key>
<string>#66CDAA</string>
<key>MediumBlue</key>
<string>#0000CD</string>
<key>MediumOrchid</key>
<string>#BA55D3</string>
<key>MediumPurple</key>
<string>#9370DB</string>
<key>MediumSeaGreen</key>
<string>#3CB371</string>
<key>MediumSlateBlue</key>
<string>#7B68EE</string>
<key>MediumSpringGreen</key>
<string>#00FA9A</string>
<key>MediumTurquoise</key>
<string>#48D1CC</string>
<key>MediumVioletRed</key>
<string>#C71585</string>
<key>MidnightBlue</key>
<string>#191970</string>
<key>MintCream</key>
<string>#F5FFFA</string>
<key>MistyRose</key>
<string>#FFE4E1</string>
<key>Moccasin</key>
<string>#FFE4B5</string>
<key>NavajoWhite</key>
<string>#FFDEAD</string>
<key>Navy</key>
<string>#000080</string>
<key>OldLace</key>
<string>#FDF5E6</string>
<key>Olive</key>
<string>#808000</string>
<key>OliveDrab</key>
<string>#6B8E23</string>
<key>Orange</key>
<string>#FFA500</string>
<key>OrangeRed</key>
<string>#FF4500</string>
<key>Orchid</key>
<string>#DA70D6</string>
<key>PaleGoldenRod</key>
<string>#EEE8AA</string>
<key>PaleGreen</key>
<string>#98FB98</string>
<key>PaleTurquoise</key>
<string>#AFEEEE</string>
<key>PaleVioletRed</key>
<string>#DB7093</string>
<key>PapayaWhip</key>
<string>#FFEFD5</string>
<key>PeachPuff</key>
<string>#FFEFD5</string>
<key>Peru</key>
<string>#CD853F</string>
<key>Pink</key>
<string>#FFC0CB</string>
<key>Plum</key>
<string>#DDA0DD</string>
<key>PowderBlue</key>
<string>#B0E0E6</string>
<key>Purple</key>
<string>#800080</string>
<key>Red</key>
<string>#FF0000</string>
<key>RosyBrown</key>
<string>#BC8F8F</string>
<key>RoyalBlue</key>
<string>#4169E1</string>
<key>SaddleBrown</key>
<string>#8B4513</string>
<key>Salmon</key>
<string>#FA8072</string>
<key>SandyBrown</key>
<string>#F4A460</string>
<key>SeaGreen</key>
<string>#2E8B57</string>
<key>SeaShell</key>
<string>#FFF5EE</string>
<key>Sienna</key>
<string>#A0522D</string>
<key>Silver</key>
<string>#C0C0C0</string>
<key>SkyBlue</key>
<string>#87CEEB</string>
<key>SlateBlue</key>
<string>#6A5ACD</string>
<key>SlateGray</key>
<string>#708090</string>
<key>Snow</key>
<string>#FFFAFA</string>
<key>SpringGreen</key>
<string>#00FF7F</string>
<key>SteelBlue</key>
<string>#4682B4</string>
<key>Tan</key>
<string>#D2B48C</string>
<key>Teal</key>
<string>#008080</string>
<key>Thistle</key>
<string>#D8BFD8</string>
<key>Tomato</key>
<string>#FF6347</string>
<key>Turquoise</key>
<string>#40E0D0</string>
<key>Violet</key>
<string>#EE82EE</string>
<key>Wheat</key>
<string>#F5DEB3</string>
<key>White</key>
<string>#FFFFFF</string>
<key>WhiteSmoke</key>
<string>#F5F5F5</string>
<key>Yellow</key>
<string>#FFFF00</string>
<key>YellowGreen</key>
<string>#9ACD32</string>
<!-- iOS UIColor Class Methods -->
<key>blackColor</key>
<string>blackColor</string>
<key>darkGrayColor</key>
<string>darkGrayColor</string>
<key>lightGrayColor</key>
<string>lightGrayColor</string>
<key>redColor</key>
<string>redColor</string>
<key>greenColor</key>
<string>greenColor</string>
<key>blueColor</key>
<string>blueColor</string>
<key>cyanColor</key>
<string>cyanColor</string>
<key>yellowColor</key>
<string>yellowColor</string>
<key>magentaColor</key>
<string>magentaColor</string>
<key>orangeColor</key>
<string>orangeColor</string>
<key>purpleColor</key>
<string>purpleColor</string>
<key>brownColor</key>
<string>brownColor</string>
<key>clearColor</key>
<string>clearColor</string>
</dict>
<key>fonts</key>
<dict>
<key>mainNavBarFont</key>
<string>Avenir:18.0</string>
<key>mainFont18</key>
<string>Avenir:18.0</string>
<key>mainFont16</key>
<string>Avenir:16.0</string>
<key>mainFont14</key>
<string>Avenir:14.0</string>
<key>mainFont12</key>
<string>Avenir:12.0</string>
<key>mainFont10</key>
<string>Avenir:10.0</string>
<key>mainFont8</key>
<string>Avenir:8.0</string>
</dict>
</dict>
</plist>
#import "UIColor+Hex.h"
@implementation UIColor (Hex)
+ (UIColor *)colorWithHex:(NSUInteger)hex;
{
hex &= 0xFFFFFF;
NSUInteger red = (hex & 0xFF0000) >> 16;
NSUInteger green = (hex & 0x00FF00) >> 8;
NSUInteger blue = hex & 0x0000FF;
return [UIColor colorWithRed:(red / 255.0f) green:(green / 255.0f) blue:(blue / 255.0f) alpha:1.0];
}
+ (UIColor *) colorWithHexString: (NSString *) hexString {
NSString *colorString = [[hexString stringByReplacingOccurrencesOfString: @"#" withString: @""] uppercaseString];
CGFloat alpha, red, blue, green;
switch ([colorString length]) {
case 3: // #RGB
alpha = 1.0f;
red = [self colorComponentFrom: colorString start: 0 length: 1];
green = [self colorComponentFrom: colorString start: 1 length: 1];
blue = [self colorComponentFrom: colorString start: 2 length: 1];
break;
case 4: // #ARGB
alpha = [self colorComponentFrom: colorString start: 0 length: 1];
red = [self colorComponentFrom: colorString start: 1 length: 1];
green = [self colorComponentFrom: colorString start: 2 length: 1];
blue = [self colorComponentFrom: colorString start: 3 length: 1];
break;
case 6: // #RRGGBB
alpha = 1.0f;
red = [self colorComponentFrom: colorString start: 0 length: 2];
green = [self colorComponentFrom: colorString start: 2 length: 2];
blue = [self colorComponentFrom: colorString start: 4 length: 2];
break;
case 8: // #AARRGGBB
alpha = [self colorComponentFrom: colorString start: 0 length: 2];
red = [self colorComponentFrom: colorString start: 2 length: 2];
green = [self colorComponentFrom: colorString start: 4 length: 2];
blue = [self colorComponentFrom: colorString start: 6 length: 2];
break;
default:
[NSException raise:@"Invalid color value" format: @"Color value %@ is invalid. It should be a hex value of the form #RBG, #ARGB, #RRGGBB, or #AARRGGBB", hexString];
break;
}
return [UIColor colorWithRed: red green: green blue: blue alpha: alpha];
}
+ (CGFloat) colorComponentFrom: (NSString *) string start: (NSUInteger) start length: (NSUInteger) length {
NSString *substring = [string substringWithRange: NSMakeRange(start, length)];
NSString *fullHex = length == 2 ? substring : [NSString stringWithFormat: @"%@%@", substring, substring];
unsigned hexComponent;
[[NSScanner scannerWithString: fullHex] scanHexInt: &hexComponent];
return hexComponent / 255.0;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment