app 背景顏色修改 --(轉(zhuǎn)發(fā))

關(guān)注倉(cāng)庫(kù)株灸,及時(shí)獲得更新:iOS-Source-Code-Analyze

從開(kāi)始寫(xiě)DKNightVersion這個(gè)框架到現(xiàn)在已經(jīng)將近一年了堤结,目前整個(gè)框架的設(shè)計(jì)也趨于穩(wěn)定芦劣。

其實(shí)夜間模式的實(shí)現(xiàn)就是相當(dāng)于多主題加顏色管理鸽捻。而最新版本的DKNightVersion已經(jīng)很好的解決了這個(gè)問(wèn)題旧烧。

在正式介紹目前版本的實(shí)現(xiàn)之前特咆,我會(huì)先簡(jiǎn)單介紹一下 1.0 時(shí)代的 DKNightVersion 的實(shí)現(xiàn)季惩,為各位讀者帶來(lái)一些新的思路,也確實(shí)想梳理一下這個(gè)框架是如何演變的腻格。

我們會(huì)以對(duì)backgroundColor為例說(shuō)明整個(gè)框架的工作原理画拾。

方法調(diào)劑的版本

如何在不改變?cè)械募軜?gòu),甚至不改變?cè)械拇a的基礎(chǔ)上菜职,為應(yīng)用優(yōu)雅地添加夜間模式成為很多開(kāi)發(fā)者不得不面對(duì)的問(wèn)題青抛。這也是 1.0 時(shí)代的 DKNightVersion 想要實(shí)現(xiàn)的目標(biāo)。

其核心思路就是使用方法調(diào)劑修改backgroundColor的存取方法酬核。

使用 nightBackgroundColor

在思考之后蜜另,我想到,想要在不改動(dòng)原有代碼的基礎(chǔ)上實(shí)現(xiàn)夜間模式只能通過(guò)在分類(lèi)中添加nightBackgroundColor屬性嫡意,并且使用方法調(diào)劑改變backgroundColor的 setter 方法举瑰。

-(void)hook_setBackgroundColor:(UIColor*)backgroundColor{if([DKNightVersionManager currentThemeVersion]==DKThemeVersionNormal){[selfsetNormalBackgroundColor:backgroundColor];}[selfhook_setBackgroundColor:backgroundColor];}

在當(dāng)前主題為DKThemeVersionNormal時(shí),將顏色保存至normalBackgroundColor中蔬螟,然后再調(diào)用原backgroundColor的 setter 方法此迅,更新視圖的顏色。

DKNightVersionManager

這里只解決了顏色設(shè)置的問(wèn)題旧巾,下面會(huì)說(shuō)明耸序,如果在主題改變時(shí),實(shí)時(shí)更新顏色鲁猩,而不用重新進(jìn)入當(dāng)前頁(yè)面坎怪。

整個(gè) DKNightVersion 都是由一個(gè)DKNightVersionManager的單例來(lái)管理的,而它的主要工作就是負(fù)責(zé)改變應(yīng)用的主題廓握、并在主題改變時(shí)通知其它視圖更新顏色

-(void)changeColor:(id)object{if([object respondsToSelector:@selector(changeColor)]){[object changeColor];}if([object respondsToSelector:@selector(subviews)]){if(![object subviews]){// Basic case, do nothing.return;}else{for(id subviewin[object subviews]){// recursive darken all the subviews of current view.[selfchangeColor:subview];if([subview respondsToSelector:@selector(changeColor)]){[subview changeColor];}}}}}

如果主題更新芋忿,那么就會(huì)遞歸地調(diào)用changeColor方法,刷新全部的視圖顏色疾棵,而這個(gè)方法的實(shí)現(xiàn)比較簡(jiǎn)單:

-(void)changeColor{if([DKNightVersionManager currentThemeVersion]==DKThemeVersionNormal){self.backgroundColor=self.normalBackgroundColor;}else{self.backgroundColor=self.nightBackgroundColor;}}

上面就是整個(gè)框架在 1.0 版本時(shí)的實(shí)現(xiàn)思路戈钢。不過(guò)這個(gè)版本的 DKNightVersion 在實(shí)際應(yīng)用中會(huì)有比較多的問(wèn)題:

在高速滾動(dòng)的scrollView上面來(lái)回切換夜間模式,會(huì)出現(xiàn)顏色錯(cuò)亂的問(wèn)題

由于對(duì)backgroundColor屬性進(jìn)行不合適的方法調(diào)劑是尔,其行為無(wú)法預(yù)測(cè)殉了,比如:在設(shè)置顏色后,再取出拟枚,不一定與設(shè)置時(shí)傳入的顏色相同

無(wú)法適配第三方 UI 控件

使用色表的版本

為了解決 1.0 中的各種問(wèn)題薪铜,我決定在 2.0 版本中放棄對(duì)nightBackgroundColor的使用众弓,并且重新設(shè)計(jì)底層的實(shí)現(xiàn),轉(zhuǎn)而使用更為穩(wěn)定隔箍、安全的方法實(shí)現(xiàn)夜間模式谓娃,先看一下效果圖:

新的實(shí)現(xiàn)不僅能夠支持夜間模式,而且能夠支持多主題蜒滩。

DKColorPicker

與上一個(gè)版本實(shí)現(xiàn)上的不同滨达,在 2.0 中刪除了全部的nightBackgroundColor,使用一個(gè)名為dk_backgroundColorPicker的屬性取代它俯艰。

@property(nonatomic,copy)DKColorPicker dk_backgroundColorPicker;

這個(gè)屬性其實(shí)就是一個(gè) block捡遍,它接收參數(shù)DKThemeVersion *themeVersion,但是會(huì)返回一個(gè)UIColor *:

在第一次傳入 picker 或者每次主題改變時(shí)竹握,都會(huì)將當(dāng)前主題DKThemeVersion傳入 picker 并執(zhí)行画株,然后,將得到的UIColor賦值給對(duì)應(yīng)的屬性backgroundColor更新視圖顏色啦辐。

typedefUIColor*(^DKColorPicker)(DKThemeVersion*themeVersion);

比如下面使用DKColorPickerWithRGB創(chuàng)建一個(gè)臨時(shí)的DKColorPicker:

在DKThemeVersionNormal時(shí)返回0xffffff

在DKThemeVersionNight時(shí)返回0x343434

在自定義的主題下返回0xfafafa(這里的順序與色表中主題的順序有關(guān))

cell.dk_backgroundColorPicker=DKColorPickerWithRGB(0xffffff,0x343434,0xfafafa);

同時(shí)谓传,每一個(gè)對(duì)象還持有一個(gè)pickers數(shù)組,來(lái)存儲(chǔ)自己的全部DKColorPicker:

@interfaceNSObject()@property(nonatomic,strong)NSMutableDictionary*pickers;@end

在第一次使用這個(gè)屬性時(shí)芹关,當(dāng)前對(duì)象注冊(cè)為DKNightVersionThemeChangingNotificaiton通知的觀察者良拼。

在每次收到通知時(shí),都會(huì)調(diào)用night_update方法充边,將當(dāng)前主題傳入DKColorPicker庸推,并再次執(zhí)行,并將結(jié)果傳入對(duì)應(yīng)的屬性[self performSelector:sel withObject:result]浇冰。

-(void)night_updateColor{[self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString*_Nonnull selector,DKColorPicker? _Nonnull picker,BOOL*_Nonnull stop){SEL sel=NSSelectorFromString(selector);id result=picker(self.dk_manager.themeVersion);[UIView animateWithDuration:DKNightVersionAnimationDuration? ? ? ? ? ? ? ? ? ? ? ? animations:^{#pragmaclang diagnostic push#pragmaclang diagnostic ignored "-Warc-performSelector-leaks"[selfperformSelector:sel withObject:result];#pragmaclang diagnostic pop}];}];}

也就是說(shuō)贬媒,在每次改變主題的時(shí)候,都會(huì)發(fā)出通知肘习。

DKColorTable

雖然我們?cè)谏厦媾R時(shí)創(chuàng)建了一些DKColorPicker际乘。不過(guò)在DKNightVersion中,我更推薦使用色表漂佩,來(lái)減少相同的DKColorPicker的創(chuàng)建脖含,并且能夠更好地管理整個(gè)應(yīng)用中的顏色:

NORMAL? NIGHT? ? RED#ffffff? #343434? #fafafa BG#aaaaaa? #313131? #aaaaaa SEP#0000ff#ffffff? #fa0000 TINT#000000#ffffff? #000000TEXT#ffffff? #444444? #ffffff BAR

上面就是默認(rèn)色表文件DKColorTable.txt中的內(nèi)容,其中投蝉,第一行表示主題养葵,NORMAL主題必須存在,而且必須為第一列瘩缆,而最右面的BG关拒、SEP就是對(duì)應(yīng)DKColorPicker的 key。

self.tableView.dk_backgroundColorPicker=DKColorPickerWithKey(BG);

在使用時(shí),上面的代碼就相當(dāng)于返回了一個(gè)在NORMAL時(shí)返回#ffffff着绊、NIGHT時(shí)返回#343434以及RED時(shí)返回#fafafa的DKColorPicker谐算。

pickerify

雖然說(shuō),我們使用色表以及DKColorPicker解決了归露,但是洲脂,到目前為止我們還沒(méi)有解決第三方框架的問(wèn)題。

比如我們使用了某個(gè)第三方框架剧包,或者自己添加了某個(gè)color屬性恐锦,比如說(shuō):

@interfaceDKView()@property(nonatomic,strong)UIColor*weirdColor;@end

weirdColor并沒(méi)有對(duì)應(yīng)的DKColorPicker,但是玄捕,我們可以通過(guò)pickerify在想要使用dk_weirdColorPicker的地方生成這個(gè)對(duì)應(yīng)的 picker:

@pickerify(DKView,weirdColor);

然后,我們就可以使用dk_weirdColorPicker屬性了:

view.dk_weirdColorPicker=DKColorPickerWithKey(BG);

pickerify其實(shí)是一個(gè)宏:

#definepickerify(KLASS, PROPERTY) interface \? ? KLASS (Night) \? ? @property (nonatomic, copy, setter = dk_set ## PROPERTY ## Picker:) DKColorPicker dk_ ## PROPERTY ## Picker; \? ? @end \? ? @interface \? ? KLASS () \? ? @property (nonatomic, strong) NSMutableDictionary *pickers; \? ? @end \? ? @implementation \? ? KLASS (Night) \? ? - (DKColorPicker)dk_ ## PROPERTY ## Picker { \? ? ? ? return objc_getAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker)); \? ? } \? ? - (void)dk_set ## PROPERTY ## Picker:(DKColorPicker)picker { \? ? ? ? objc_setAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); \? ? ? ? [self setValue:picker(self.dk_manager.themeVersion) forKeyPath:@keypath(self, PROPERTY)];\? ? ? ? [self.pickers setValue:[picker copy] forKey:_DKSetterWithPROPERTYerty(@#PROPERTY)]; \? ? } \? ? @end

這個(gè)宏根據(jù)傳入的類(lèi)和屬性名棚放,為我們生成了對(duì)應(yīng)picker的存取方法枚粘,它也可以說(shuō)是一種元編程的手段。

這里生成的 setter 方法不是標(biāo)準(zhǔn)意義上的駝峰命名法dk_setweirdColorPicker:飘蚯,因?yàn)槲也恢涝趺床拍茏尨髮?xiě)首字母之后的屬性添加到這里(如果各位讀者有解決方案馍迄,歡迎提 PR 或者 issue)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末局骤,一起剝皮案震驚了整個(gè)濱河市攀圈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌峦甩,老刑警劉巖赘来,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異凯傲,居然都是意外死亡犬辰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)冰单,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)幌缝,“玉大人,你說(shuō)我怎么就攤上這事诫欠『眩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵荒叼,是天一觀的道長(zhǎng)轿偎。 經(jīng)常有香客問(wèn)我,道長(zhǎng)被廓,這世上最難降的妖魔是什么贴硫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上英遭,老公的妹妹穿的比我還像新娘间护。我一直安慰自己,他們只是感情好挖诸,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布汁尺。 她就那樣靜靜地躺著,像睡著了一般多律。 火紅的嫁衣襯著肌膚如雪痴突。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天狼荞,我揣著相機(jī)與錄音辽装,去河邊找鬼。 笑死相味,一個(gè)胖子當(dāng)著我的面吹牛拾积,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丰涉,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼拓巧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了一死?” 一聲冷哼從身側(cè)響起肛度,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎投慈,沒(méi)想到半個(gè)月后承耿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伪煤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年瘩绒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片带族。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锁荔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蝙砌,到底是詐尸還是另有隱情阳堕,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布择克,位于F島的核電站恬总,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏肚邢。R本人自食惡果不足惜壹堰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一拭卿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贱纠,春花似錦峻厚、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至辖试,卻和暖如春辜王,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背罐孝。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工呐馆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人莲兢。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓汹来,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親怒见。 傳聞我的和親對(duì)象是個(gè)殘疾皇子俗慈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容