亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

iOS實現換膚功能的簡單處理框架(附源碼)

2020-07-26 02:21:22
字體:
來源:轉載
供稿:網友

前言

換膚功能是在APP開發過程中遇到的比較多的場景,為了提供更好的用戶體驗,許多APP會為用戶提供切換主題的功能。主題顏色管理涉及到的的步驟有

  • 顏色配置
  • 使用顏色
  • UI元素動態變更的能力
  • 動態修改配置
  • 主題包管理
  • 如何實施
  • 優化

效果如下:

DEMO代碼:https://gitee.com/dhar/iosdemos/tree/master/YTThemeManagerDemo

顏色配置

因為涉及到多種配置,所以以代碼的方式定義顏色實踐和維護的難度是比較高的,一種合適的方案是--顏色的配置是通過配置文件的形式進行導入的。配置文件會經過轉換步驟,最終形成代碼層級的配置,以全局的方式提供給各個模塊使用,這里會涉及到一個顏色管理者的概念,一般地這回事一個單例對象,提供全局訪問的接口。同一個APP中在不同的模塊中保存不同的主題顏色配置,在不同的層級中也可以存在不同的主題顏色配置,因為涉及到層級間的配置差異,所以顏色的配置需要引入一個等級的概念,一般地較高層級顏色的配置等級是高于較低層級的,存在相同的配置較高層級的配置會覆蓋較低層級的配置。

我們采用的顏色配置的文件形如下面所示,為什么是在一個json文件的colorkey下面呢,是為了考慮到未來的擴展性,如果不同的主題會涉及到一些尺寸值的差異化,我們可以添加dimensionskey進行擴展配置。

{ "color": { "Black_A":"323232", "Black_AT":"323232", "Black_B":"888888", "Black_BT":"888888", "White_A":"ffffff", "White_AT":"ffffff", "White_AN":"ffffff", "Red_A":"ff87a0", "Red_AT":"ff87a0", "Red_B":"ff5073", "Red_BT":"ff5073", "Colour_A":"377ce4", "Colour_B":"6aaafa", "Colour_C":"ff8c55", "Colour_D":"ffa200", "Colour_E":"c4a27a", }}

有了以上的配置,顏色配置的工作主要就是解析該配置文件,把配置保存在一個單例對象中即可,這部分主要的步驟如下:

  • 配置文件類表根據等級排序
  • 獲取每個配置文件中的配置,進行保存
  • 通知外部主題顏色配置發生改變

對應的代碼如下,這里有個需要注意的地方是,加載配置文件的時候使用了文件讀寫鎖進行讀寫的鎖定操作,防止讀臟數據的發生,直到配置文件加載完成,釋放讀寫鎖,這時讀進程可以繼續。

- (void)loadConfigWithFileName:(NSString *)fileName level:(NSInteger)level { if (fileName.length == 0) { return; }  pthread_rwlock_wrlock(&_rwlock); __block BOOL finded = NO; [self.configFileQueue enumerateObjectsUsingBlock:^(YTThemeConfigFile *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { if ([obj.fileName isEqualToString:fileName]) {  finded = YES;  *stop = YES; } }]; if (!finded) { // 新增配置文件 YTThemeConfigFile *file = [[YTThemeConfigFile alloc] init]; file.fileName = fileName; file.level = level; [self.configFileQueue addObject:file]; // 優先級排序 [self.configFileQueue sortUsingComparator:^NSComparisonResult(YTThemeConfigFile *_Nonnull obj1, YTThemeConfigFile *_Nonnull obj2) {  if (obj1.level > obj2.level) {  return NSOrderedDescending;  }  return NSOrderedAscending; }]; [self setupConfigFilesContainDefault:YES]; } pthread_rwlock_unlock(&_rwlock);}- (void)setupConfigFilesContainDefault:(BOOL)containDefault { NSMutableDictionary *defaultColorDict = nil, *currentColorDict = nil;  // 加載默認配置 if (containDefault) { defaultColorDict = [NSMutableDictionary dictionary]; [self loadConfigDataWithColorMap:defaultColorDict valueMap:nil isDefault:YES];  self.defaultColorMap = defaultColorDict; }  // 加載主題配置 if (_themePath.length > 0) { currentColorDict = [NSMutableDictionary dictionary]; [self loadConfigDataWithColorMap:currentColorDict valueMap:nil isDefault:NO];  self.currentColorMap = currentColorDict; }  // 發送主體顏色變更通知 [self notifyThemeDidChange];}- (void)notifyThemeDidChange { NSArray *allActionObjects = self.actionMap.objectEnumerator.allObjects; for (YTThemeAction *action in allActionObjects) { [action notifyThemeDidChange]; }}- (void)loadConfigDataWithColorMap:(NSMutableDictionary *)colorMap valueMap:(NSMutableDictionary *)valueMap isDefault:(BOOL)isDefault { // 每一次新增一個配置文件,所有配置文件都得重新計算一次,這里有很多重復多余的工作 [self.configFileQueue enumerateObjectsUsingBlock:^(YTThemeConfigFile *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { NSDictionary *dict = nil; if (isDefault) {  dict = obj.defaultDict; } else {  dict = obj.currentDict; } if (dict.count > 0) {  [self loadThemeColorTo:colorMap from:dict]; // 將所有配置表中的color字段的數據都放到colorMap中 } }];}- (void)loadThemeColorTo:(NSMutableDictionary *)dictionary from:(NSDictionary *)from { NSDictionary<NSString *, NSString *> *colors = from[@"color"]; [colors enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, NSString *_Nonnull obj, BOOL *_Nonnull stop) { // 十六進制字符串轉為UIColor UIColor *color = [UIColor yt_nullcolorWithHexString:obj]; if (color) {  [dictionary setObject:color forKey:key]; } else {  [dictionary setObject:obj forKey:key]; } }];}

管理者處理處理配置之外,還需要暴露外部接口給客戶端使用,以用于獲取不同主題下對應的顏色色值、圖片資源、尺寸信息等和主題相關的信息。比如我們會提供一個colorForKey方法獲取不同主題下的同一個key對應的顏色色值,獲取色值的大致步驟如下:

  • 從當前的主題配置中獲取
  • 從默認的主題配置中獲取
  • 從預留的主題配置中獲取
  • 如果重定向的配置,遞歸處理
  • 以上步驟都完成還未找到返回默認黑色

這里使用了讀寫鎖的寫鎖,如果同時有寫操作獲取了該鎖,讀取進程會阻塞直到寫操作的完成釋放鎖。

/** 獲取顏色值 */- (UIColor *)colorForKey:(NSString *)key { pthread_rwlock_rdlock(&_rwlock); UIColor *color = [self colorForKey:key isReserveKey:NO redirectCount:0]; pthread_rwlock_unlock(&_rwlock); return color;}- (UIColor *)colorForKey:(NSString *)key isReserveKey:(BOOL)isReserveKey redirectCount:(NSInteger)redirectCount { if (key == nil) { return nil; }  ///正常獲取色值 id colorObj = [_currentColorMap objectForKey:key]; if (colorObj == nil) { colorObj = [_defaultColorMap objectForKey:key]; }  if (isReserveKey && colorObj == nil) { return nil; }  ///看看是否有替補key if (colorObj == nil) { NSString *reserveKey = [_reserveKeyMap objectForKey:key]; if (reserveKey) {  colorObj = [self colorForKey:reserveKey isReserveKey:YES redirectCount:redirectCount]; } }  ///查看當前key 能否轉成 color if (colorObj == nil) { colorObj = [UIColor yt_colorWithHexString:key]; }  if ([colorObj isKindOfClass:[UIColor class]]) { ///如果是 重定向 或者 替補 key 的color 要設置到 當前 colorDict 里面 // 重定向的配置形如:"Red_A":"Red_B", if (redirectCount > 0 || isReserveKey) {  [_currentColorMap ?: _defaultColorMap setObject:colorObj forKey:key]; } return colorObj; } else { if (redirectCount < 3) { // 重定向遞歸  return [self colorForKey:colorObj isReserveKey:NO redirectCount:redirectCount + 1]; } else {  return [UIColor blackColor]; } }}

使用顏色

顏色的使用也是經由管理者的,為了方便,定義一個顏色宏提供給客戶端使用

#define YTThemeColor(key) ([[YTThemeManager sharedInstance] colorForKey:key])

客戶端使用的代碼如下:

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, 200, 40)];label.text = @"Text";label.textColor = YTThemeColor(kCK_Red_A);label.backgroundColor = YTThemeColor(kCK_Black_H);[self.view addSubview:label];

另外,因為顏色配置的key為字符串類型,直接使用字符串常量并不是個好辦法,所以把對應的字符串轉換為宏定義是一個相對好的辦法。第一個是方便使用,可以使用代碼提示;第二個是不容易出錯,特別是長的字符串;第三個也會一定程度上的提高效率。

YTColorDefine類的宏定義

// .h 中的聲明///BlackFOUNDATION_EXTERN NSString *kCK_Black_A;FOUNDATION_EXTERN NSString *kCK_Black_AT;FOUNDATION_EXTERN NSString *kCK_Black_B;FOUNDATION_EXTERN NSString *kCK_Black_BT;// .m 中的定義NSString *kCK_Black_A = @"Black_A";NSString *kCK_Black_AT = @"Black_AT";NSString *kCK_Black_B = @"Black_B";NSString *kCK_Black_BT = @"Black_BT";

主題包管理

在實際的落地項目中,主題包管理涉及到的事項包括主題包下載和解壓和動態加載主題包等內容,最后的一步是更換主題配置文件所在的配置路徑,為了演示的方便,我們會把不同主題的資源放置在bundle中某一個特定的文件夾下,通過切換管理者中的主題路徑配置來達到切換主題的效果,和動態下載更換主題的步驟是一樣的。

管理者提供一個設置主題配置的配置路徑的方法,在該方法中改變配置路徑的同時,重新加載配置即可,代碼如下

/** 設置主題文件的路徑 @param themePath 文件的路徑 */- (void)setupThemePath:(NSString *)themePath { pthread_rwlock_wrlock(&_rwlock);  _themePath = [themePath copy];  self.currentColorMap = nil;  if ([_themePath.lowercaseString isEqualToString:[[NSBundle mainBundle] resourcePath].lowercaseString]) { _themePath = nil; }  self.currentThemePath = _themePath;  for (int i = 0; i < self.configFileQueue.count; i++) { YTThemeConfigFile *obj = [self.configFileQueue objectAtIndex:i]; [obj resetCurrentDict]; } [self setupConfigFilesContainDefault:NO];  pthread_rwlock_unlock(&_rwlock);}

如何實施

以上的流程涉及到的只是iOS平臺下的一個技術解決方案,真實的實踐過程中會涉及到安卓平臺、Web頁面、UI出圖的標注,這些是要進行統一處理的,才能在各個端上有一致的體驗。第一步就是制定合理的顏色規范,把規范同步給各個端的利益相關人員;第二部是UI出圖顏色是規范的顏色定義值,而不是比如#ffffff這樣的顏色,需要是比如White_A這樣規范的顏色定義值,這樣客戶端處理使用的就是White_A這個值,不用管在不同主題下不同的顏色表現形式。

優化

loadConfigDataWithColorMap方法調用的優化

如果模塊很多,每個模塊都會調用loadConfigWithFileName加載配置文件,那么loadConfigDataWithColorMap方法處理文件的時間復雜度是O(N*N),會重復處理很多多余的工作,理想的做法是底層保存一份公有的顏色配置,然后在APP層加載一份定制化的配置,在模塊中不用再加載主題配置文件,這樣會提高效率。

附:讀寫鎖pthread_rwlock_t的使用

讀寫鎖是用來解決讀者寫者問題的,讀操作可以共享,寫操作是排他的,讀可以有多個在讀,寫只有唯一個在寫,同時寫的時候不允許讀。

具有強讀者同步和強寫者同步兩種形式

強讀者同步:當寫者沒有進行寫操作,讀者就可以訪問;

強寫者同步:當所有寫者都寫完之后,才能進行讀操作,讀者需要最新的信息,一些事實性較高的系統可能會用到該所,比如定票之類的。

讀寫鎖的操作:

讀寫鎖的初始化:

        定義讀寫鎖:          pthread_rwlock_t  m_rw_lock;

        函數原型:              pthread_rwlock_init(pthread_rwlock_t * ,pthread_rwattr_t *);

        返回值:0,表示成功,非0為一錯誤碼

讀寫鎖的銷毀:

        函數原型:             pthread_rwlock_destroy(pthread_rwlock_t* );

        返回值:0,表示成功,非0表示錯誤碼

獲取讀寫鎖的讀鎖操作:分為阻塞式獲取和非阻塞式獲取,如果讀寫鎖由一個寫者持有,則讀線程會阻塞直至寫入者釋放讀寫鎖。

        阻塞式:

                            函數原型:pthread_rwlock_rdlock(pthread_rwlock_t*);

        非阻塞式:

                            函數原型:pthread_rwlock_tryrdlock(pthread_rwlock_t*);

       返回值: 0,表示成功,非0表示錯誤碼,非阻塞會返回ebusy而不會讓線程等待

獲取讀寫鎖的寫鎖操作:分為阻塞和非阻塞,如果對應的讀寫鎖被其它寫者持有,或者讀寫鎖被讀者持有,該線程都會阻塞等待。

      阻塞式:

                           函數原型:pthread_rwlock_wrlock(pthread_rwlock_t*);

      非阻塞式:

                           函數原型:pthread_rwlock_trywrlock(pthread_rwlock_t*);

       返回值: 0,表示成功

釋放讀寫鎖:

                         函數原型:pthread_rwlock_unlock(pthread_rwlock_t*);

總結(轉):

互斥鎖與讀寫鎖的區別:

當訪問臨界區資源時(訪問的含義包括所有的操作:讀和寫),需要上互斥鎖;

當對數據(互斥鎖中的臨界區資源)進行讀取時,需要上讀取鎖,當對數據進行寫入時,需要上寫入鎖。

讀寫鎖的優點:

對于讀數據比修改數據頻繁的應用,用讀寫鎖代替互斥鎖可以提高效率。因為使用互斥鎖時,即使是讀出數據(相當于操作臨界區資源)都要上互斥鎖,而采用讀寫鎖,則可以在任一時刻允許多個讀出者存在,提高了更高的并發度,同時在某個寫入者修改數據期間保護該數據,以免任何其它讀出者或寫入者的干擾。

讀寫鎖描述:

獲取一個讀寫鎖用于讀稱為共享鎖,獲取一個讀寫鎖用于寫稱為獨占鎖,因此這種對于某個給定資源的共享訪問也稱為共享-獨占上鎖。

有關這種類型問題(多個讀出者和一個寫入者)的其它說法有讀出者與寫入者問題以及多讀出者-單寫入者鎖。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美在线视频观看免费网站| 成人精品视频在线| 日韩高清电影免费观看完整| 在线观看视频亚洲| 中文字幕精品www乱入免费视频| 热久久99这里有精品| 中文字幕9999| www.久久久久久.com| 精品亚洲男同gayvideo网站| 91久久精品美女| 久久精品国产69国产精品亚洲| 亚洲人成绝费网站色www| 91高清视频免费观看| 国产日韩av在线| 大胆人体色综合| 国产精品网红直播| 伊人伊成久久人综合网小说| 亚洲色图25p| 国产成人一区二| 国产精欧美一区二区三区| 国产精品自拍网| 久久精品这里热有精品| 日韩成人av在线| 国模精品视频一区二区三区| 国产精品久久久久久久久粉嫩av| 668精品在线视频| 欧美激情中文字幕在线| 国产丝袜视频一区| 国产一区二区三区在线看| 国产精品一香蕉国产线看观看| 51精品在线观看| 成人免费福利视频| 国产精品女主播| 欧美视频免费在线| 91久久久久久久久久久久久| 亚洲摸下面视频| 最近2019年好看中文字幕视频| 91久久国产婷婷一区二区| 亚洲另类欧美自拍| 欧美午夜激情小视频| 日本一区二区不卡| 欧美成人一区二区三区电影| 欧美日韩中文字幕| 亚洲性xxxx| 亚洲天堂网在线观看| 欧美电影在线免费观看网站| 上原亚衣av一区二区三区| 亚洲天堂第一页| 国产精品久久久久久久久久久久久久| 日韩视频在线免费| 亚洲第一偷拍网| 欧美电影免费观看| 国产日韩欧美成人| 亚洲摸下面视频| 欧美日韩日本国产| 亚洲iv一区二区三区| 日韩av电影在线免费播放| 久久99久久99精品免观看粉嫩| 亚洲天堂第一页| 97高清免费视频| 成人在线小视频| 91久久精品日日躁夜夜躁国产| 免费av在线一区| 国产精品入口夜色视频大尺度| 55夜色66夜色国产精品视频| 高清欧美性猛交| 亚洲国产日韩欧美在线图片| 亚洲一区二区三区成人在线视频精品| 国产亚洲精品激情久久| 最近中文字幕日韩精品| 国产精品一区二区av影院萌芽| 青青草国产精品一区二区| 久久久久女教师免费一区| 黑人巨大精品欧美一区二区免费| 日韩精品免费综合视频在线播放| 国产美女精品视频免费观看| 国产精品v日韩精品| 91精品久久久久久久久| 久精品免费视频| 成人啪啪免费看| 欧美激情视频在线观看| 成人福利在线观看| 欧美自拍视频在线| 欧美日韩aaaa| 91九色国产视频| 国产高清视频一区三区| 91国产美女视频| 亚洲精品丝袜日韩| 亚洲国产天堂久久综合网| 欧美黑人巨大xxx极品| 大伊人狠狠躁夜夜躁av一区| 精品国偷自产在线视频99| 91精品国产自产在线观看永久| 成人免费观看49www在线观看| 日韩精品有码在线观看| 欧美性xxxx极品hd欧美风情| 九九视频这里只有精品| 一区二区三区无码高清视频| 国产精品亚洲综合天堂夜夜| 57pao成人国产永久免费| 亚洲精品综合精品自拍| 亚洲乱码一区av黑人高潮| 亚洲国产精彩中文乱码av在线播放| 午夜精品视频在线| 国产97色在线|日韩| 日韩欧美亚洲综合| 在线电影欧美日韩一区二区私密| 欧美在线欧美在线| 亚洲www永久成人夜色| 综合欧美国产视频二区| 亚洲男人av在线| 久久久久久久激情视频| 亚洲国产精品va| 不卡在线观看电视剧完整版| 国产日韩中文在线| 久久伊人精品视频| 欧美在线视频观看免费网站| 亚洲第一av在线| 亚洲精品成人av| 国产成人精品免高潮在线观看| 91在线高清免费观看| 欧美激情小视频| 日韩精品欧美激情| 国产精品视频永久免费播放| 亚洲最大福利视频网| 97精品在线观看| 777午夜精品福利在线观看| 韩国v欧美v日本v亚洲| 久久人人爽人人爽人人片av高请| 日韩精品中文字幕在线播放| 亚洲国产成人av在线| 亚洲精品av在线播放| 精品动漫一区二区三区| 丝袜美腿精品国产二区| 久色乳综合思思在线视频| 亚洲福利视频久久| 日韩电影免费在线观看中文字幕| 国产精品91在线| 国产成人精品a视频一区www| 亚洲成人网久久久| 日韩国产在线播放| 久久久精品一区二区三区| 精品亚洲一区二区三区在线观看| 久久精品99久久香蕉国产色戒| 亚洲图片在区色| 欧美整片在线观看| 欧美国产视频日韩| 国产成人久久久精品一区| 国产欧美日韩中文字幕| 欧美精品在线网站| 96sao精品视频在线观看| 久久久久久成人精品| 日韩成人黄色av| 日本精品一区二区三区在线播放视频| 色综合伊人色综合网| 日韩亚洲欧美成人| 91精品视频在线看| 色妞久久福利网| 亚洲精品久久久久| 中文字幕精品一区久久久久| 96pao国产成视频永久免费| 成人午夜激情免费视频| 欧美电影在线免费观看网站| 亚洲国产精久久久久久|