? ? ? ?主題的切換就是可以增強(qiáng)用戶體驗(yàn)、結(jié)合運(yùn)營活動(dòng)的一個(gè)點(diǎn):譬如 QQ 的夜間模式,節(jié)日里電商 APP 的皮膚切換等等的這些小細(xì)節(jié)往往就是贏得用戶尊重的根本。
? ? ? ?讓 APP 已有的控件能切換主題可以用子類化,swizzle 或 category 來實(shí)現(xiàn),其中子類化和 category 實(shí)現(xiàn)起來差不多,都是讓控件調(diào)特定的方法達(dá)到切換風(fēng)格的效果,而 swizzle 的影響范圍會(huì)比較廣,使用的時(shí)候可以通過 Associated Object 添加一個(gè)標(biāo)記值,讓需要切換風(fēng)格的控件設(shè)置這個(gè)標(biāo)記值,讓標(biāo)記值來決定是否需要 swizzle??紤]到上述幾種方案的復(fù)雜度,最后選擇了 category 來實(shí)現(xiàn)。
? ? ? ?主題管理類的核心功能就是負(fù)責(zé)主題的更新,切換。正如下圖所示,想讓主題管理類通知到這么多待切換的 category 并不是一件容易的事,因?yàn)橛X得在 category 上添加觀察者并不是太好的設(shè)計(jì),你很難知道什么時(shí)機(jī)該把觀察者移除了。

這也就意味著,可能需要自己動(dòng)手來實(shí)現(xiàn)回調(diào)機(jī)制了,讓切換主題相關(guān)的 category 通過主題管理類注冊(cè)一個(gè)回調(diào) block,主題類維護(hù)使用一個(gè)字典維護(hù)這些 block,待切換時(shí)由主題管理類統(tǒng)一回調(diào),達(dá)到類似 Notification 的效果。
UILabel的categroy


只是在方法的底部添加了注冊(cè) block 的方法,而注冊(cè) block 的方法也十分簡(jiǎn)單,只需依據(jù) key 判斷下是否需要將 block 加入代碼中。
那么問題來了,到底該如何設(shè)計(jì)一個(gè)這樣的 key 呢?
同一個(gè)控件的主題 category 有多個(gè)需要切換主題的方法(例如 UIButton 有setTitleColor:forState: 和 setImage:forState:);
多個(gè)控件都是通過同一個(gè) categroy 來切換主題(例如有多個(gè) UIButton 需要切換主題);
其實(shí)統(tǒng)籌來看,就是如何通過某個(gè)類的實(shí)例和所需定制主題的方法來確定一個(gè) key。
一開始很自然的拼了一個(gè)類的地址和方法名來作為key[NSString stringWithFormat:@"%p#%@", class, NSStringFromSelector(selector)]
流程能跑起來了,但是問題也很明顯,只知道一個(gè)對(duì)象的指針字符串,根據(jù)對(duì)象是否被釋放而進(jìn)行的字典清理將變得難以實(shí)現(xiàn):

那么,應(yīng)該怎樣設(shè)計(jì) block 對(duì)應(yīng)的 key 呢?
能從 key 中獲取到注冊(cè)的類;
key 中也存有方法做 key 的唯一性和對(duì)象訪問該方法安全性的校驗(yàn)respondsToSelector;
為此,實(shí)現(xiàn)了一個(gè)輔助的 model,用以訪問需要注冊(cè)的對(duì)象實(shí)例和方法名,同時(shí)作為 Dictionary 的 key,它還需要實(shí)現(xiàn) NSCoping 協(xié)議:

weak 修飾的對(duì)象實(shí)例能夠在對(duì)象被釋放后自動(dòng)置 nil,下面附上最初的.m文件實(shí)現(xiàn)。

效果圖

總結(jié)
? ? ? ?本文描述了實(shí)現(xiàn)一個(gè)主題管理類的大致思路,希望能對(duì)讀者有所幫助。后來筆者想到既然有了 target 和 selector,能不能通過 NSInvocation 來動(dòng)態(tài)調(diào)用,就不借助 block 來回調(diào)了,在嘗試中筆者 NSInvocation 的效率的確會(huì)低一點(diǎn)。用 block 可以很靈活的指定好需要調(diào)用什么方法?;蛟S,也可以通過實(shí)現(xiàn)一個(gè) weak proxy 的方式使用 Notification 來實(shí)現(xiàn),筆者就沒有嘗試了,感興趣的讀者可以試試。
源碼地址:https://github.com/AlbertXYZ/ThemeDemo