暗黑模式(iOS多主题)完美设计实现

Introduce:

        ios暗黑模式,多主题多皮肤设计,用于解决在ios上实现可跟随系统主题变更,也可不跟随实现自定义主题模式设计。本sdk可以支持任意多个主题。开发习惯上极度契合ios开发习惯,对于复杂的主题设置类,均可使用对象的tkThemeChangeBlock进行回调设置变更。优势:

1.代码无侵入,轻量级SDK。上手简单。完美解决多任务后台主题即时变更,屏幕快照变更主体。

2.全局控制,效率极高。用hashmap的形式管理block指针对象,轻量。

3.一切OC对象皆tkThemeChangeBlock主题回调,根据主题的目前的索引变更主题。

4.常用的UIKit的组件,赋予主题属性,主题方法等。直接设置主题颜色、图片数组即可。完全契合系统设置属性,方法习惯。

5.设计原理完美,弱引用的形式回调,不会对项目产生内存泄漏,内存占用不释放问题。UIKit-View层即用即注册,跟随对象释放即销毁。主题回调block即用即注册,跟随对象释放即销毁。

movie show

效果视频如下:(ps:没办法,简书只支持优酷/腾讯视频  没法去广告 想看大视频,点链接:https://v.youku.com/v_show/id_XNDcwNTcxODMwNA==.html


iOSDarkDemo.mp4

How To Get Started

1.导入  pod  'TKThemeConfig'        //(ps: 本人会对sdk一直维护,放心使用 )

2.在使用到的地方 #import <TKThemeConfig/TKThemeConfig.h>

程序加载完毕初始化

How To Usage

1.便捷用法

      sdk封装可常见的view控件。对于这些常见的控件,比如CALayer,UIView,UIImageView,UIButton, UILabel等15个组件进行了贴合开发者的属性定制,在原属性上加前缀TKTheme。能满足80%以上的开发需求。 剩下的用万能方法即可。有时间功夫的小伙伴可以加入一起完善便捷用法哦。 示例如下: // UIButton like //UIImageView like //CALayer like 。其它的可自行看头文件

layer.tkThemebackgroundColors = @[UIColor.brownColor,UIColor.darkGrayColor];

[themeSeting setTkThemebackgroundColors:@[UIColor.redColor,UIColor.brownColor]];

[imageViewsetTkThemeimages:@[[UIImage imageNamed:@"001.jpg"],[UIImage imageNamed:@"002.jpg"]]];

2.万能用法

      一切皆object对象,一切对象具备tkThemeChangeBlock(NSObject+TKUpdate.h),变更主题会触发 任意对象的tkThemeChangeBlock回调,可以在这个回调做主题设置。 本回调为主线程,进行UI主题变更,但是不要做耗时操作,耗时操作放到异步非主线程即可。 示例如下: //view.tkThemeChangeBlock //navigationBar.navigationBar

self.view.tkThemeChangeBlock= ^(id  _Nullableitself,NSUIntegerthemeIndex) { //设置主题    }

self.navigationController.navigationBar.tkThemeChangeBlock = ^(id  _Nullable itself, NSUInteger themeIndex) { //设置主题  }

项目地址:https://github.com/Tkoul/TKThemeConfig

本文为原创,啃了几天泡面搞出来的。转载注明出处。喜欢的给个小星星。对不足的地方,可以一起交流,提升。邮件:Tkoull@163.com

对简书编辑器玩不转,有点丑!设计思想后续补上。


PS:设计思路 2020-11-6

鉴于目前直逼6K的阅读量,再懒我也得补上承诺的设计思路以及思想。(😶  我是真的懒)。

API怎么设计?多主题拓展怎么设计?NO,NO,NO 对于我这种懒人来说,不讲这些基础东西了。咱们就讲多主题设计的核心:

1.怎么跟随设置,同步的去变更所有的展示层UI以及栈里面的视图UI.

2.改变主题的即时性

3.对主app的性能影响

4.代码执行效率

5.对主APP代码的侵入

以上以重要程度列出。主体变更,联动所有的UI变更。

实现思路一:通知

有人说简单,通知完事,实际上业界内很多开源的就是这么设计实现的。写一个根类或者拓展类,注册通知不就OK啦,然而真的这么简单吗?

通知实现原理:通知实际上是利用runtime动态的创建类,创建的规则是监听某个类的属性时,会动态的创建这个类的子类,并且初始化也是对这个子类操作的。比如现在有AClass,监听它的属性,runtime会临时动态的创建它的子类,如subAclass(实际是有一套规则,好像是noti—aclass,别在意细节,了解就行),同时把AClass的isa指针指向subAClass。那么后续,你对AClass所有的操作,方法调用,实例化等其实都是在操作subAClass。你打印实例对象的Class的时候,依然返回的是AClass,什么?不是说isa都是指向subAClass了吗?对的,没错,只不过为了很像,这种情况,系统api重写了class方法,强制的反回了AClass。表面上看着对象是AClass,操作的却是subAClass,实际都是假象,为了达到看着这样的效果,设计这个模式的大神是做了很多蒙蔽我们的工作。在subAClass里面会重写监听的属性的方法,达到监听效果。

敲桌子1:回味下,注册一个通知的代价。。。

继续:每次注册通知,都会在通知中心重新注册一次,即使是同一对象,监听同一个消息,而不是去覆盖原来的监听。这样,当通知中心转发某一消息时,如果同一对象多次注册了这个通知的观察者,则会收到多个通知。

敲桌子2:如果写for循环推出来10000个UIViewContrl,每个UIViewContrl上有很多子view,那么这时候会注册多少通知,对多少个类进行动态变更,创建呢?思考下。

结论:通知量级很重,通知是在某些场景下做通知,而不是用在这个场景。也不建议在开发过程中注册太多的通知,避免滥用。

百度做的性能对比:(借鉴下数据😀)

条件:在上万个视图量级下真机iphone5s初始化CPU消耗初始化耗时!

系统暗黑          CPU消耗:50%    初始化耗时:3312ms

HashTable        CPU消耗:50%    初始化耗时:3341ms

Notification    CPU消耗:99%    初始化耗时:5115ms

实现思路二:重写object类,用全局hashmap记录所有的栈里的对象。存起来,变更主体设置,再去这个hasmap里面读所有的对象变更主题。有这么简单吗?hashmap(我们的字典,数组均叫hashmap,不行就hasarry呗,别在意细节😀)

1.存时机  初始化的时候存,还是加载到视图存,是共性全存还是需要的视图去存。

2.循环引用问题。 NSMutableArry存的东西那么就会强制引用,那么不去手动释放,移除,会导致视图不会被释放,内存泄漏。有的小伙伴说,我存weak指针,不好意思,存进来了,你敢释放,我NSMutableArry就敢崩溃给你看。敲桌子,留给爱探索的小好伙伴(那么我weak对象+NSPointerArray)呢?tips NSPointerArray和我们常用的数组一样,但是他可以存空对象,为什么不用呢,甚至在开发中几乎很难看到这个容器对象。

3.release时机和把控会很难

结论:依赖hashmap直接存储对象以及通过UIView的didAddSubview,didMoveToSuperview重写方法等很难实现。即时实现,也不是好的设计,有点类似为解决问题而解决问题。同时对APP代码入侵严重,内存释放同样不好把控。

以上囊括了业内的大多设计思路和实现。在我看来,均不是很理想。

通知方式虽然方便,入侵少,但是效率性能消耗极大,被我首先pass掉。思路二,效率ok,性能略有欠缺,入侵性大,错误率高,稳定性差,内存容易泄露,会连环导致均不被释放。

怎么做到轻量,效率高,性能好,完全稳定,我们就需要各管各的,无论怎么设计,谁干谁的事情,谁改变对别人没影响。TKThemeConfig设计实现,就具备这些特性。

即用即创建,一管理,一嗅探,一释放。(专业点😀 :哨兵)

1.即用即创建:对象需要具备变更主题的能力,但是在创建的时候才会具备这个能力。并且只在初始化创建一次。

2.hashMap管理。管理的不是跟主题相关的对象(一般指继承UIView的视图,但是有很多继承obj的对象也具备主题颜色设置,这也是思路二无法万能的痛点),而是对象实例方法内部变量block。结合反向block方式。在类初始化方法创建block,同时全局hashMap(TKThemeConfig使用了单利)储存该blcok。 解决了全局hashmap跟该对象的引用关系,即它俩没关系。存的是方法内部初始化的blcok,block内部用的是对象的weak,不相互持有。这里涉及三者,1.全局hashmap  2.对象实例方法内部block(兴趣的可以研究下globblock,staticblock,mallocblock),3.对象本身。三者只有hashmap和block强持有,block和对象弱持有。hashmap和对象无关。对象在自己的生命周期内,释放时机均自己把控即依然遵循系统ARC管理,无侵入,完美!就是这个关键设计解决最核心的问题。

3.一嗅探,一释放。由于对象和blcok弱关系,那么视图对象释放掉,对于视图对象而言就结束了,但是block还被hashmap强持有,怎么去释放它呢?敲桌子,嗅探-(我觉得是天才灵感,有木有😀),在一段时间去反向回调blcok,去试探与他相关的weak类对象是否还在,一旦嗅探到weak==nil,说明跟block引用的对象已经释放了,那么blcok就告诉hashmap说,哥们,我管理的对象不在了,我也没啥用了,你把我弄死吧。那么hashmap就把blcok取出来置空,杀死,释放回收blcok所占的内存。具体实现,去down源码阅读即可,嗅探我这设置的15秒一次,因为我允许hashmap多持有blcok一会,blcok本身就是一个很轻量的元素,存的一个指针而已,占内存可以忽略。同时遍历很快,遍历上万次并移除都不到2毫秒,在牛的app也不会有上万个视图层吧😀。ok,这就是TKThemeConfig设计思想。

敲黑板,这是一种思想,可以应用于类似场景的任何地方,一种解决思路,也是灵感所致。希望对读者有所启发。读懂了会对你很有帮助。

推荐阅读更多精彩内容