286

关于iOS App换肤的几种方式

 6 years ago
source link: http://www.cocoachina.com/ios/20171012/20762.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

关于iOS App换肤的几种方式

四门三连· 2017-10-12 阅读数 10773
本文来自 执着_7748 ,作者 四门三连

本地换肤

本地换肤,是通过包里面自身已经拥有的图片和颜色配置,对视图以及各控件切换主题色。

优点是工作量相对较少,不需要后台人员配合。

缺点是局限性比较大,必须通过发包的形式才能更新最新的皮肤颜色,也不能根据节日更换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文件,再切换。

demo地址:https://github.com/LKY7748/ChangeSkinDemo

参考资料:https://github.com/yanjunz/ThemeManager


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK