iOS换肤方案

换肤作为一个增强用户体验的功能,现已成为大厂APP的一个标配,包括:

1、局部换肤功能:白天夜间模式的切换,运营活动各种图标的更换,字体大小的设置

2、全局换肤功能:皮肤商城主题切换

无论是哪种,核心问题都涉及到动态换肤,下面基于这个问题提出了一个通用的解决方案。

一个视图的效果是由布局、资源两部分构成,布局指的是视图的位置,大小;资源指的是颜色、字体、图片、动画等。像H5和Android平台,视图的资源通过配置文件进行独立配置,系统提供了资源自动映射的功能,也就是系统本身支持了资源的动态切换,iOS在这点上需要手动实现了。

动态换肤是对视图的资源进行动态更换,涉及到以下几个关键点:

换什么,可更换的资源。基本上人眼可见的都是可以更换的,像颜色、字体、图片、动画等。

主要的UI对象, UILabel, UIButton, UIImageView, UITableView , UIView, 导航栏 , tabbar:
1.切换背景图片
2.控件的背景色
3.更改文字颜色,文字字体

如何查找资源,资源包路径、结构和内容,如何映射资源到视图上。

怎么换,资源如何动态设置到视图上。

资源定义

资源包括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"];

也可以不通过类别来处理,通过 一个全新的接口获取  (皮肤管理员) , 在这里根据用户选定的皮肤 , 加载指定的颜色 , 指定的图片。

SkinManager 的.h文件

SkinManager 的.m文件

资源映射

在资源定义好后,需要根据定义的key从对应的皮肤包中找到具体的资源。颜色映射到具体的色值,字体映射到具体的fontName和fontSize,这两者通过plist文件、或者json配置实现即可,图片和动画等直接已对应的文件形式提供即可。最终资源包的结构和内容如下:

接下来,需要进行资源的映射,比如将Color的Key值映射到Color.plist里面对应的Color值。iOS资源包叫Bundle,资源默认是放在mainBundle里,通过获取对应皮肤的Bundle就可以拿到对应的Color.plist文件,进而获取最终的色值。如果是默认皮肤,对应的就是mainBundle,如果切换到另一个皮肤,将该皮肤下载后切换到下载的bundle就可以了。

{
	"navigation": {
		"color": "0xEEEEEE"
	},
	"tabbar": [{
		"id": 1,
		"image": "图片名1",
		"frame": "{16,44,13,33}",
		"textColor": "0xEEEEEE"
	}, {
		"id": 2,
		"image": "图片名2",
		"frame": "{16,44,13,33}",
		"textColor": "0xEEEEEE"
	}],
    "normalPage":{
        "bg_grey_1":"0xcccccc",
        "bg_grey_2":"0xDDDDDD",
        "bg_grey_3":"0xc1c1c1",
        "bg_grey_4":"0xcccccc",
        "bg_grey_5":"0xcccccc",
	    "text_white_1":"0xFFFFFF",
	    "text_white_2":"0x233242",
	    "text_white_3":"0xFF3F1F",
	    "text_white_4":"0x1FF32",
	    "text_white_5":"0xDFF32",
	    "text_white_6":"0xAFF32",
 
    },
}

热切换

假定一个视图在换肤时只需要动态更换一个背景颜色,我们通常会这样做:

//创建并初始化视图,默认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通用换肤解决方案

Github: 
SwiftTheme/README_CN.md at master · wxxsw/SwiftTheme · GitHub

GitHub - SlashDevelopers/SDTheme: 一款轻量级的iOS皮肤主题切换方案

JXTheme:iOS9+换肤/暗黑模式最佳方案之一,轻量级、高度自定义、swift编写 - 掘金

GitHub - guochaoshun/changeSkin  验证效果demo地址

另一种方案:不用runtime的方法,通过通知然后在各个场景修改:iOS程序框架设计之皮肤切换功能 - 珲少的个人空间 - OSCHINA - 中文开源技术交流社区,  iOS App主题皮肤切换功能简介和具体实现详解 附有源码_主题功能 简介_风流 少年的博客-CSDN博客

系统的暗黑模式:iOS 13.0 暗黑模式的适配_ios 暗黑模式适配_same_life的博客-CSDN博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值