2017-05-22 13:27:54 -07:00

368 lines
8.4 KiB
Objective-C

//
// VSTheme.m
// Q Branch LLC
//
// Created by Brent Simmons on 6/26/13.
// Copyright (c) 2012 Q Branch LLC. All rights reserved.
//
#import "VSTheme.h"
#if TARGET_OS_IPHONE
#define VS_COLOR UIColor
#define VS_IMAGE UIImage
#define VS_FONT UIFont
#define VS_EDGE_INSETS UIEdgeInsets
#define VSEdgeInsetsMake UIEdgeInsetsMake
#else /*Mac*/
#define VS_COLOR NSColor
#define VS_IMAGE NSImage
#define VS_FONT NSFont
#define VS_EDGE_INSETS NSEdgeInsets
#define VSEdgeInsetsMake NSEdgeInsetsMake
#endif
static BOOL stringIsEmpty(NSString *s);
static VS_COLOR *colorWithHexString(NSString *hexString);
@interface VSTheme ()
@property (nonatomic, strong) NSDictionary *themeDictionary;
@property (nonatomic, strong) NSMutableDictionary *colorCache;
@property (nonatomic, strong) NSMutableDictionary *colorWithAlphaCache;
@property (nonatomic, strong) NSMutableDictionary *fontCache;
@end
@implementation VSTheme
#pragma mark Init
- (id)initWithDictionary:(NSDictionary *)themeDictionary {
self = [super init];
if (self == nil)
return nil;
_themeDictionary = themeDictionary;
_colorCache = [NSMutableDictionary new];
_fontCache = [NSMutableDictionary new];
return self;
}
- (id)objectForKey:(NSString *)key {
id obj = [self.themeDictionary valueForKeyPath:key];
if (obj == nil && self.parentTheme != nil)
obj = [self.parentTheme objectForKey:key];
return obj;
}
- (BOOL)boolForKey:(NSString *)key {
id obj = [self objectForKey:key];
if (obj == nil)
return NO;
return [obj boolValue];
}
- (NSString *)stringForKey:(NSString *)key {
id obj = [self objectForKey:key];
if (obj == nil)
return nil;
if ([obj isKindOfClass:[NSString class]])
return obj;
if ([obj isKindOfClass:[NSNumber class]])
return [obj stringValue];
return nil;
}
- (NSInteger)integerForKey:(NSString *)key {
id obj = [self objectForKey:key];
if (obj == nil)
return 0;
return [obj integerValue];
}
- (CGFloat)floatForKey:(NSString *)key {
id obj = [self objectForKey:key];
if (obj == nil)
return 0.0f;
return [obj floatValue];
}
- (NSTimeInterval)timeIntervalForKey:(NSString *)key {
id obj = [self objectForKey:key];
if (obj == nil)
return 0.0;
return [obj doubleValue];
}
- (VS_IMAGE *)imageForKey:(NSString *)key {
NSString *imageName = [self stringForKey:key];
if (stringIsEmpty(imageName))
return nil;
return [VS_IMAGE imageNamed:imageName];
}
- (VS_COLOR *)colorForKey:(NSString *)key {
VS_COLOR *cachedColor = self.colorCache[key];
if (cachedColor != nil)
return cachedColor;
NSString *colorString = [self stringForKey:key];
VS_COLOR *color = nil;
if ([colorString isEqualToString:@"clear"])
color = [VS_COLOR clearColor];
else
color = colorWithHexString(colorString);
if (color == nil)
color = [VS_COLOR blackColor];
self.colorCache[key] = color;
return color;
}
- (VS_COLOR *)colorWithAlphaForKey:(NSString *)key {
VS_COLOR *cachedColor = self.colorWithAlphaCache[key];
if (cachedColor != nil) {
return cachedColor;
}
VS_COLOR *color = [self colorForKey:key];
CGFloat alpha = [self floatForKey:[key stringByAppendingString:@"Alpha"]];
color = [color colorWithAlphaComponent:alpha];
self.colorWithAlphaCache[key] = color;
return color;
}
- (VS_EDGE_INSETS)edgeInsetsForKey:(NSString *)key {
CGFloat left = [self floatForKey:[key stringByAppendingString:@"Left"]];
CGFloat top = [self floatForKey:[key stringByAppendingString:@"Top"]];
CGFloat right = [self floatForKey:[key stringByAppendingString:@"Right"]];
CGFloat bottom = [self floatForKey:[key stringByAppendingString:@"Bottom"]];
VS_EDGE_INSETS edgeInsets = VSEdgeInsetsMake(top, left, bottom, right);
return edgeInsets;
}
- (VS_FONT *)fontForKey:(NSString *)key {
VS_FONT *cachedFont = self.fontCache[key];
if (cachedFont != nil)
return cachedFont;
NSString *fontName = [self stringForKey:key];
CGFloat fontSize = [self floatForKey:[key stringByAppendingString:@"Size"]];
if (fontSize < 1.0f)
fontSize = 15.0f;
VS_FONT *font = nil;
if (stringIsEmpty(fontName))
font = [VS_FONT systemFontOfSize:fontSize];
else
font = [VS_FONT fontWithName:fontName size:fontSize];
if (font == nil)
font = [VS_FONT systemFontOfSize:fontSize];
self.fontCache[key] = font;
return font;
}
- (CGPoint)pointForKey:(NSString *)key {
CGFloat pointX = [self floatForKey:[key stringByAppendingString:@"X"]];
CGFloat pointY = [self floatForKey:[key stringByAppendingString:@"Y"]];
CGPoint point = CGPointMake(pointX, pointY);
return point;
}
- (CGSize)sizeForKey:(NSString *)key {
CGFloat width = [self floatForKey:[key stringByAppendingString:@"Width"]];
CGFloat height = [self floatForKey:[key stringByAppendingString:@"Height"]];
CGSize size = CGSizeMake(width, height);
return size;
}
#if TARGET_OS_IPHONE
- (UIViewAnimationOptions)curveForKey:(NSString *)key {
NSString *curveString = [self stringForKey:key];
if (stringIsEmpty(curveString))
return UIViewAnimationOptionCurveEaseInOut;
curveString = [curveString lowercaseString];
if ([curveString isEqualToString:@"easeinout"])
return UIViewAnimationOptionCurveEaseInOut;
else if ([curveString isEqualToString:@"easeout"])
return UIViewAnimationOptionCurveEaseOut;
else if ([curveString isEqualToString:@"easein"])
return UIViewAnimationOptionCurveEaseIn;
else if ([curveString isEqualToString:@"linear"])
return UIViewAnimationOptionCurveLinear;
return UIViewAnimationOptionCurveEaseInOut;
}
- (VSAnimationSpecifier *)animationSpecifierForKey:(NSString *)key {
VSAnimationSpecifier *animationSpecifier = [VSAnimationSpecifier new];
animationSpecifier.duration = [self timeIntervalForKey:[key stringByAppendingString:@"Duration"]];
animationSpecifier.delay = [self timeIntervalForKey:[key stringByAppendingString:@"Delay"]];
animationSpecifier.curve = [self curveForKey:[key stringByAppendingString:@"Curve"]];
return animationSpecifier;
}
#endif
- (VSTextCaseTransform)textCaseTransformForKey:(NSString *)key {
NSString *s = [self stringForKey:key];
if (s == nil)
return VSTextCaseTransformNone;
if ([s caseInsensitiveCompare:@"lowercase"] == NSOrderedSame)
return VSTextCaseTransformLower;
else if ([s caseInsensitiveCompare:@"uppercase"] == NSOrderedSame)
return VSTextCaseTransformUpper;
return VSTextCaseTransformNone;
}
- (NSString *)string:(NSString *)s transformedWithTextCaseTransformKey:(NSString *)key {
VSTextCaseTransform textCaseTransform = [self textCaseTransformForKey:key];
NSString *transformedString = nil;
switch (textCaseTransform) {
case VSTextCaseTransformNone:
transformedString = s;
break;
case VSTextCaseTransformLower:
transformedString = [s lowercaseString];
break;
case VSTextCaseTransformUpper:
transformedString = [s uppercaseString];
break;
default:
break;
}
return transformedString;
}
@end
NSString *VSThemeSpecifierPlusKey(NSString *specifier, NSString *key) {
return [NSString stringWithFormat:@"%@.%@", specifier, key];
}
#if TARGET_OS_IPHONE
@implementation VSTheme (Animations)
- (void)animateWithAnimationSpecifierKey:(NSString *)animationSpecifierKey animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion {
VSAnimationSpecifier *animationSpecifier = [self animationSpecifierForKey:animationSpecifierKey];
[UIView animateWithDuration:animationSpecifier.duration delay:animationSpecifier.delay options:animationSpecifier.curve animations:animations completion:completion];
}
@end
#pragma mark -
@implementation VSAnimationSpecifier
@end
#endif
static BOOL stringIsEmpty(NSString *s) {
return s == nil || [s length] == 0;
}
static VS_COLOR *colorWithHexString(NSString *hexString) {
/*Picky. Crashes by design.*/
if (stringIsEmpty(hexString))
return [VS_COLOR blackColor];
NSMutableString *s = [hexString mutableCopy];
[s replaceOccurrencesOfString:@"#" withString:@"" options:0 range:NSMakeRange(0, [hexString length])];
CFStringTrimWhitespace((__bridge CFMutableStringRef)s);
NSString *redString = [s substringToIndex:2];
NSString *greenString = [s substringWithRange:NSMakeRange(2, 2)];
NSString *blueString = [s substringWithRange:NSMakeRange(4, 2)];
unsigned int red = 0, green = 0, blue = 0;
[[NSScanner scannerWithString:redString] scanHexInt:&red];
[[NSScanner scannerWithString:greenString] scanHexInt:&green];
[[NSScanner scannerWithString:blueString] scanHexInt:&blue];
return [VS_COLOR colorWithRed:(CGFloat)red/255.0f green:(CGFloat)green/255.0f blue:(CGFloat)blue/255.0f alpha:1.0f];
}