app 換膚
關(guān)于app換膚的幾種方式
本地?fù)Q膚
本地?fù)Q膚,是通過(guò)包里面自身已經(jīng)擁有的圖片和顏色配置,對(duì)視圖以及各控件切換主題色。
優(yōu)點(diǎn)是工作量相對(duì)較少,不需要后臺(tái)人員配合。
缺點(diǎn)是局限性比較大,必須通過(guò)發(fā)包的形式才能更新最新的皮膚顏色,也不能根據(jù)節(jié)日更換app主題。
遠(yuǎn)程換膚
遠(yuǎn)程換膚,是通過(guò)后臺(tái)配置的方式,下載新的皮膚對(duì)app重新布局。也可以在app當(dāng)天的第一次啟動(dòng)時(shí),發(fā)送請(qǐng)求給后臺(tái),通過(guò)后臺(tái)的服務(wù)器時(shí)間,緩存最新的節(jié)日皮膚。并在節(jié)日當(dāng)天換上最新的皮膚。
優(yōu)點(diǎn)是可以靈活配置各種皮膚,并且也不會(huì)占用太多的安裝包大小。
缺點(diǎn)是工作量大,耗時(shí)長(zhǎng),需要后臺(tái)人員配合。
換膚方案
app換膚,有多種途徑實(shí)現(xiàn)換膚的效果,以下所敘的是通過(guò)runtime給UIView添加一個(gè)屬性字典,在屬性字典中設(shè)置可變皮膚的特性。再采用通知的方式,讓每個(gè)控件去刷新自身,并且由自身控制生命周期。從而實(shí)現(xiàn)本地?fù)Q膚的效果,并且兼容后續(xù)添加遠(yuǎn)程換膚。
首先設(shè)置存取皮膚主題的plist文件,默認(rèn)添加幾個(gè)皮膚方案。再新建一個(gè)類(lèi)控制皮膚切換時(shí)更改顏色和圖片
+ (UIImage *)setSkinImageWithStr:(NSString *)str
{
// 獲取plist里面的數(shù)據(jù)
NSDictionary * dict = [NSString getDicWithKey:@"Skin.plist"];
if ([dict.allKeys containsObject:@"selectColor"])
{
NSString * key = [dict objectForKey:@"selectColor"];
return [UIImage imageNamed:[NSString stringWithFormat:@"%@_%@", str, key]];
}
return nil;
}
+ (UIColor *)setSkinColorWithStr:(NSString *)str
{
NSDictionary * dict = [NSString getDicWithKey:@"Skin.plist"];
if ([dict.allKeys containsObject:@"selectColor"])
{
NSString * key = [dict objectForKey:@"selectColor"];
NSDictionary * dic = [dict objectForKey:key];
NSString * colorStr = [dic objectForKey:@"color"];
return [UIColor sam_colorWithHex:colorStr];
}
return [UIColor clearColor];
}
給UIView和UITabbarItem添加類(lèi)別,利用runtime添加themeMap的屬性
static void *kUIView_ThemeMap;
static void *kUIView_DeallocHelper;
@implementation UITabBarItem (UIConfig)
- (void)setThemeMap:(NSDictionary *)themeMap
{
objc_setAssociatedObject(self, &kUIView_ThemeMap, themeMap, OBJC_ASSOCIATION_COPY_NONATOMIC);
if (themeMap) {
@autoreleasepool {
// Need to removeObserver in dealloc
if (objc_getAssociatedObject(self, &kUIView_DeallocHelper) == nil) {
__unsafe_unretained typeof(self) weakSelf = self; // NOTE: need to be __unsafe_unretained because __weak var will be reset to nil in dealloc
id deallocHelper = [self addDeallocBlock:^{
NSLog(@"deallocing %@", weakSelf);
[[NSNotificationCenter defaultCenter] removeObserver:weakSelf];
}];
objc_setAssociatedObject(self, &kUIView_DeallocHelper, deallocHelper, OBJC_ASSOCIATION_ASSIGN);
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:kThemeDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(themeChanged) name:kThemeDidChangeNotification object:nil];
[self themeChanged];
}
}
else {
[[NSNotificationCenter defaultCenter] removeObserver:self name:kThemeDidChangeNotification object:nil];
}
}
- (NSDictionary *)themeMap
{
return objc_getAssociatedObject(self, &kUIView_ThemeMap);
}
添加key值對(duì)應(yīng)UItabbarItem的選中顏色,未選中顏色,選中圖片,未選中圖片
/**
* 設(shè)置圖片
*/
NSString *kTabBarItemKeyImageName = @"kTabBarItemKeyImageName";
/**
* 設(shè)置選中圖片
*/
NSString *kTabBarItemKeySelectedImageName = @"kTabBarItemKeySelectedImageName";
/**
* 設(shè)置文字顏色
*/
NSString *kTabBarItemKeyColorName = @"kTabBarItemKeyColorName";
/**
* 設(shè)置選中文字顏色
*/
NSString *kTabBarItemKeySelectedColorName = @"kTabBarItemKeySelectedColorName";
通知監(jiān)聽(tīng)的方法
#define TColor(name) [ChangeSkinManager setSkinColorWithStr:name]
#define TImage(name) [ChangeSkinManager setSkinImageWithStr:name]
- (void)themeChanged
{
// TODO: performace tuning
dispatch_async(dispatch_get_main_queue(), ^{
[self changeTheme];
});
}
- (void)changeTheme
{
NSDictionary *map = self.themeMap;
if (map[kTabBarItemKeyImageName])
{
self.image = TImage(map[kTabBarItemKeyImageName]);
}
if (map[kTabBarItemKeySelectedImageName])
{
self.selectedImage = TImage(map[kTabBarItemKeySelectedImageName]);
}
if (map[kTabBarItemKeyColorName])
{
[self setTitleTextAttributes:[NSMutableDictionary dictionaryWithObjectsAndKeys:TColor(map[kTabBarItemKeyColorName]) ,NSForegroundColorAttributeName, nil] forState:UIControlStateNormal];
}
if (map[kTabBarItemKeySelectedColorName])
{
[self setTitleTextAttributes:[NSMutableDictionary dictionaryWithObjectsAndKeys:TColor(map[kTabBarItemKeySelectedColorName]) ,NSForegroundColorAttributeName, nil] forState:UIControlStateSelected];
}
}
在AppDelegate設(shè)置tabbar的時(shí)候
OneViewController * oneVC = [[OneViewController alloc] init];
UINavigationController * oneNav = [[UINavigationController alloc] initWithRootViewController:oneVC];
oneNav.tabBarItem.title = @"第一頁(yè)";
[oneNav.tabBarItem setTitlePositionAdjustment:UIOffsetMake(0, -3)];
// @""里面設(shè)置plist文件里面自定義的key
oneNav.tabBarItem.themeMap = @{
kTabBarItemKeyColorName:@"",
kTabBarItemKeySelectedColorName:[ChangeSkinManager setSkinColorWithStr:@""]
};
最后在更換主題時(shí),發(fā)送通知就可以了
- (void)changeSkin:(UIButton *)button
{
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] ;
NSString *infoFilePath = [cachesPath stringByAppendingPathComponent:@"Skin.plist"];
NSDictionary * dic = [NSString getDicWithKey:@"Skin.plist"];
[dic setValue:@"red" forKey:@"selectColor"];
[dic writeToFile:infoFilePath atomically:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:kThemeDidChangeNotification object:nil];
}
總結(jié)
如需要實(shí)現(xiàn)遠(yuǎn)程換膚,還需要添加后臺(tái)支持,下載之后添加到plist文件,再切換。
demo地址:https://github.com/LKY7748/ChangeSkinDemo
參考資料:https://github.com/yanjunz/ThemeManager
把工程重新整理了一遍,可以下載查看了。。。