本地换肤
本地换肤,是通过包里面自身已经拥有的图片和颜色配置,对视图以及各控件切换主题色。
优点是工作量相对较少,不需要后台人员配合。
缺点是局限性比较大,必须通过发包的形式才能更新最新的皮肤颜色,也不能根据节日更换app主题。
远程换肤
远程换肤,是通过后台配置的方式,下载新的皮肤对app重新布局。也可以在app当天的第一次启动时,发送请求给后台,通过后台的服务器时间,缓存最新的节日皮肤。并在节日当天换上最新的皮肤。
优点是可以灵活配置各种皮肤,并且也不会占用太多的安装包大小。
缺点是工作量大,耗时长,需要后台人员配合。
换肤方案
app换肤,有多种途径实现换肤的效果,以下所叙的是通过runtime给UIView添加一个属性字典,在属性字典中设置可变皮肤的特性。再采用通知的方式,让每个控件去刷新自身,并且由自身控制生命周期。从而实现本地换肤的效果,并且兼容后续添加远程换肤。
首先设置存取皮肤主题的plist文件,默认添加几个皮肤方案。再新建一个类控制皮肤切换时更改颜色和图片
+ (UIImage *)setSkinImageWithStr:(NSString *)str { // 获取plist里面的数据 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添加类别,利用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值对应UItabbarItem的选中颜色,未选中颜色,选中图片,未选中图片
/** * 设置图片 */ NSString *kTabBarItemKeyImageName = @"kTabBarItemKeyImageName"; /** * 设置选中图片 */ NSString *kTabBarItemKeySelectedImageName = @"kTabBarItemKeySelectedImageName"; /** * 设置文字颜色 */ NSString *kTabBarItemKeyColorName = @"kTabBarItemKeyColorName"; /** * 设置选中文字颜色 */ NSString *kTabBarItemKeySelectedColorName = @"kTabBarItemKeySelectedColorName";
通知监听的方法
#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设置tabbar的时候
OneViewController * oneVC = [[OneViewController alloc] init]; UINavigationController * oneNav = [[UINavigationController alloc] initWithRootViewController:oneVC]; oneNav.tabBarItem.title = @"第一页"; [oneNav.tabBarItem setTitlePositionAdjustment:UIOffsetMake(0, -3)]; // @""里面设置plist文件里面自定义的key oneNav.tabBarItem.themeMap = @{ kTabBarItemKeyColorName:@"", kTabBarItemKeySelectedColorName:[ChangeSkinManager setSkinColorWithStr:@""] };
最后在更换主题时,发送通知就可以了
- (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]; }
总结
如需要实现远程换肤,还需要添加后台支持,下载之后添加到plist文件,再切换。