iOS换肤方案
Posted 想名真难
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS换肤方案相关的知识,希望对你有一定的参考价值。
换肤作为一个增强用户体验的功能,现已成为大厂APP的一个标配,包括:
1、局部换肤功能:白天夜间模式的切换,活动日各种图标的更换,字体大小的设置
2、全局换肤功能:皮肤商城主题切换
无论是哪种,核心问题都涉及到动态换肤,下面基于这个问题提出了一个通用的解决方案。
一个视图的效果是由布局、资源两部分构成,布局指的是视图的位置,大小;资源指的是颜色、字体、图片、动画等。像H5和android平台,视图的资源通过配置文件进行独立配置,系统提供了资源自动映射的功能,也就是系统本身支持了资源的动态切换,ios在这点上需要手动实现了。
动态换肤是对视图的资源进行动态更换,涉及到以下几个关键点:
换什么,可更换的资源。基本上人眼可见的都是可以更换的,像颜色、字体、图片、动画等。
如何查找资源,资源包路径、结构和内容,如何映射资源到视图上。
怎么换,资源如何动态设置到视图上。
资源定义
资源包括Color、Font、Image、动画等,系统提供创建这些资源的API是这样的:
UIColor *color = [UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:0.8];
UIFont *font = [UIFont systemFontOfSize:14];
UIImage *image = [UIImage imageNamed:@"img"];
以Color为例,系统提供创建Color的API中参数都是确定的数字,对于数字是什么我们其实不用太关心,我们需要关心的是这个Color代表的含义,比如导航栏的颜色,那么可以将这个色值映射到导航栏颜色这个key上,这样就可进行配置,进而达到动态替换的目的。
最终,我们会将颜色,图片,字体等都通过key值的方式进行设置,结果如下:
//navigationColor=>#EEEEEE
UIColor *color = [UIColor colorWithKey:@"navigationColor"];
//navigationTitleFont=>PingFang-SC-Regular size:14
UIFont *font = [UIFont fontWithKey:@"navigationTitleFont"];
//navigationBg=>navigationBg.png
UIImage *image = [UIImage imageWithKey:@"navigationBg"];
资源映射
在资源定义好后,需要根据定义的key从对应的皮肤包中找到具体的资源。颜色映射到具体的色值,字体映射到具体的fontName和fontSize,这两者通过plist文件实现即可,图片和动画等直接已对应的文件形式提供即可。最终资源包的结构和内容如下:
接下来,需要进行资源的映射,比如将Color的Key值映射到Color.plist里面对应的Color值。iOS资源包叫Bundle,资源默认是放在mainBundle里,通过获取对应皮肤的Bundle就可以拿到对应的Color.plist文件,进而获取最终的色值。如果是默认皮肤,对应的就是mainBundle,如果切换到另一个皮肤,将该皮肤下载后切换到下载的bundle就可以了。
热切换
假定一个视图在换肤时只需要动态更换一个背景颜色,我们通常会这样做:
//创建并初始化视图,默认color0=>white
UIView *view = [UIView alloc] init];
view.backgroundColor = [UIColor colorWithKey:@"color0"];
//切换背景色,重新调用一次设置背景色的方法,color0=>black
view.backgroundColor = [UIColor colorWithKey:@"color0"];
当然,一个APP的换肤功能不可能简单到仅仅只换一处颜色,如果有很多处的话,我们是不可能每一处都重复去调用对应的设置资源的方法的。
可以在对视图进行资源设置时,记录下视图、设置资源的方法和所需的参数,然后在动态切换时,将记录的方法自动重复执行即可。iOS提供了NSInvocation类,可以进行方法的动态调用。还是以上面的例子为例,采用NSInvocation,swizzle setBackgroundColor:方法,在调用时可以这样做:
//1、创建NSInvocation
NSMethodSignature *sign = [UIView.class instanceMethodSignatureForSelector:@selector(setBackgroundColor:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sign];
//2、保存参数colorKey
//invocation需要参数color,但是此时只有colorKey,在执行具体调用时通过colorKey获取color再进行调用,其他动态参数类似,比如图片,字体等。
//3、执行时先通过[UIColor colorWithKey:colorKey]获取Color,设置Invocation color参数
//4、调用invocation设置View的背景色
[invocation invokeWithTarget:view];
view会保存它所有的资源设置的invocations,这样在动态切换时循环执行invocations,最终View的资源切换的数据结构如下:
Arg是不可变参数,如UINavigationBar的setBackgroundImage:forBarMetrics:中的barMetrics参数,ArgBuilder用于构建可变参数,比如颜色,字体,动画等等。另外ArgBuilder是链式结构,因为一个可变的参数可能通过多次调用转化而来,比如一张原始图片,需要进行裁剪,拉伸,旋转后才得到最终的图片,那么这个图片需要执行多次ArgBuilder操作,最终才得到结果图片。
面对一个问题时,我们首先需要分析思考其本质,把握住了本质,剩下的就只是coding了。换肤的本质是资源的动态设置,如何定义资源、如何动态设置就是解决这个问题的核心。
以上是关于iOS换肤方案的主要内容,如果未能解决你的问题,请参考以下文章