iOS主題設計

iOS主題

第一種方案思路:

業務方:
  • 添加監聽主題變更通知, 在通知方法里面進行重置主題
主題管理者業務:
  • 主題的配置
  • 發送主題變更通知

第二種方案思路
業務方:
  • 配置自己業務的各個主題的色值和資源
  • 向管理員注冊切換主題的回調Block/Selector,在回調里面重置主題
主題管理者業務:
  • 集合去保存每一個業務的回調,key為業務本身, value為回調Block/Selector
  • 變更主題去觸發所有注冊的業務回調

第三種方案( 不建議 )
直接替換主工程window的root控制器,直接全部重新創建

優缺點分析
  • 第一種方案需要業務去注冊通知, 代碼優化可采用分類的形式添加注冊和注銷通知的快捷方法,方便編碼. 詬病也在這里需要自己去注冊之后再寫響應通知的方法

  • 第二種方案需要每一個使用主題的地方都需要去設置自己的主題配置, 業務方使用起來太麻煩, 組件化之后后期需要改動的地方太多

合并一下前兩種方案

采用第一種管理層做法: 由管理層去管理主題的配置, 并增加第二種管理層的服務: 注冊回調功能

這樣之后的方案就是

業務方:
  • 向管理員注冊切換主題的回調Block/Selector,在回調里面重置主題
主題管理層:
  • 主題的配置
  • 集合去保存每一個業務的回調,key為業務本身, value為回調Block/Selector
  • 變更主題去觸發所有注冊的業務回調
這樣之后業務方使用方便, 組件化采用協議注冊形式達到解耦主題管理者和業務

代碼思路圖示

image-20211214102528845.png

由上圖導出下圖具體代碼

image-20211214102517079.png

代碼講解:

數據協議

基礎主題協議: 這是定義業務使用主題的字段
 @protocol ThemeBaseStyleProtocol <NSObject>
 
 /// tabbar的背景顏色
 @property (nonatomic , strong) UIColor *tabbar_bg_color;
 /// navigation背景顏色
 @property (nonatomic , strong) UIColor *navigation_bg_color;
 /// tabbar未讀數的背景顏色
 @property (nonatomic , strong) UIColor *tabbar_unread_num_bg_color;
 /// tabbar未讀數的文案顏色
 @property (nonatomic , strong) UIColor *tabbar_unread_num_title_color;
 
 /// 分模塊
 #pragma mark - Home
 /// 首頁選項文字顏色
 @property (nonatomic , strong) UIColor *home_navigation_enum_title_color;
 /// 首頁選項文字選中顏色
 @property (nonatomic , strong) UIColor *home_navigation_enum_selected_title_color;
 /// 首頁選項文案右邊的箭頭默認圖片
 @property (nonatomic , strong) NSString *home_navigation_enum_normal_arrow_image_name;
 /// 首頁選項文案右邊的箭頭選中圖片
 @property (nonatomic , strong) NSString *home_navigation_enum_selected_arrow_image_name;
 /// 首頁選項滑動條顏色
 @property (nonatomic , strong) UIColor *home_navigation_enum_silder_color;
 /// 首頁右上角加號圖片name
 @property (nonatomic , strong) NSString *home_navigation_right_add_img_name;
 
 #pragma mark - Service
 /// 頂部標題顏色
 @property (nonatomic , strong) UIColor *service_navigation_title_color;
 @property (nonatomic , strong) UIColor *service_navigation_subtitle_color;
 @property (nonatomic , strong) NSString *service_navigation_arrow_image_name;
 
 #pragma mark - Partner
 
 #pragma mark - MAll
 /// 商場的頂部大標題顏色
 @property (nonatomic , strong) UIColor *mall_navigation_left_title_color;
 /// 頂部右邊搜索按鈕圖片
 @property (nonatomic , strong) NSString *mall_navigation_right_search_image_name;
 /// 頂部右邊購物車按鈕圖片
 @property (nonatomic , strong) NSString *mall_navigation_right_shopping_cart_image_name;
 /// 頂部購物車數值顏色
 @property (nonatomic , strong) UIColor *mall_navigation_shoping_num_color;
 /// 頂部購物車數值背景顏色
 @property (nonatomic , strong) UIColor *mall_navigation_shoping_num_bg_color;
 
 #pragma mark - My
 /// 頂部背景圖片
 @property (nonatomic , strong) NSString *my_navigation_top_bg_image_name;
 /// 人頭裝飾
 @property (nonatomic , strong) NSString *my_header_decoration_image_name;
 
 /// 預留給網絡數據轉化為本地數據
 - (instancetype)initWithThemeConfig:(NSDictionary *)config;
 
 @end
主題擴展協議: 這里是為了給主題配置一個包裝層, 供擴展用,例如遠程數據和本地數據的相互兼容
@interface ThemeItemModel : NSObject
 
 @property (nonatomic , strong) NSString *themeId;
 
 @property (nonatomic , strong) NSString *title;
 
 @property (nonatomic , strong) NSString *img;
 
 @property (nonatomic , strong) id<ThemeBaseStyleProtocol> theme;
 
 @end

管理者

管理者協議
  /// 主題
 @protocol ThemeProtocol <NSObject>
 
 /// 當前所有的皮膚
 @property (nonatomic , strong) NSMutableArray <ThemeItemModel *> *allThemes;
 
 /// 當前的主題, 默認: ThemeDetaultStyle
 @property (nonatomic , strong, readonly) id<ThemeBaseStyleProtocol>currentTheme;
 
 /// 注冊主題改變之后的回調
 /// 注冊可以采用分類方法, 寫了快捷方式
 /// 想要刪除回調,可設置回調為nil即可
 /// 不會對obj進行強引用, 外界obj釋放掉之后,里面對應的回調也會銷毀
 /// 內部會先自動調用一次這個回調
 /// @param obj 響應者
 /// @param callback 回調
 - (void)regisObserver:(id _Nonnull)obj themeDidChangeCallBack:(void(^ _Nullable)(id<ThemeBaseStyleProtocol> theme))callback;
 
 /// 更新皮膚配置, 從后臺獲取是否有新的皮膚, 以保存到本地區使用, 本地預存了兩份
 - (void)updateConfigTheme;
 
 /// 更換皮膚
 - (void)changeTheme:(ThemeItemModel *)themeItemModel;
 
 @end
快捷業務方法
@interface UIView (ThemeCategory)
 
 - (void)regisThemeDidChangeCallBack:(void(^)(id<ThemeBaseStyleProtocol> theme))callback;
 
 @end
 
 @interface UIViewController (ThemeCategory)
 
 - (void)regisThemeDidChangeCallBack:(void(^)(id<ThemeBaseStyleProtocol> theme))callback;
 
 @end
 
 @implementation UIView (ThemeCategory)
 
 - (void)regisThemeDidChangeCallBack:(void(^)(id<ThemeBaseStyleProtocol> theme))callback{

     id <ThemeProtocol>theme = 獲取到實現ThemeProtocol 協議的實現者, 必須單類;

    [theme regisObserver:self themeDidChangeCallBack:callback];
 }
 
 @end
 
 @implementation UIViewController (ThemeCategory)
 
 - (void)regisThemeDidChangeCallBack:(void(^)(id<ThemeBaseStyleProtocol> theme))callback{

     id <ThemeProtocol>theme = 獲取到實現ThemeProtocol 協議的實現者, 必須單類;

    [theme regisObserver:self themeDidChangeCallBack:callback];
 }
 
 @end

生成協議之后,基本上數據流已經通了

數據流走向?
① 程序啟動,配置主題
② 業務(UIView/UIViewController)去注冊協議,
③當主題發生變化的時候,調用主題管理者的changeTheme 傳入當前要改變的主題, 主題管理者會觸發注冊的所有回調, 業務方在回調里面根據回調的當前主題去設置新的UI樣式, 如圖
image-20211214104635144.png

管理者實現代碼

①配置數據
 /// 配置主題
 - (void)updateConfigTheme; {

    [self.allThemes removeAllObjects];
     // 默認
     ThemeItemModel *defaultModel = [ThemeItemModel new];
     defaultModel.title = @"默認";
     defaultModel.themeId = @"18c63459a2c069022c7790430f761214";// 默認 MD5
   //ThemeDetaultStyle 這個就是實現了主題基礎協議的數據類,返回了主題的顏色和圖片
     defaultModel.theme = [ThemeDetaultStyle new];
    [self.allThemes addObject:defaultModel];
     _currentTheme = defaultModel.theme;
     // 新年
     ThemeItemModel *yearModel = [ThemeItemModel new];
     yearModel.title = @"新年";
     yearModel.themeId = @"d46d5bb15db17203e4e5d61372325f91";// 新年 MD5
   //ThemeNewYearStyle 這個就是實現了主題基礎協議的數據類,返回了主題的顏色和圖片
     yearModel.theme = [ThemeNewYearStyle new];
    [self.allThemes addObject:yearModel];

     // 先看本地是否有保存皮膚
     NSString *saveKey = [[NSUserDefaults standardUserDefaults] objectForKey:ThemeSaveKey];

     if ([saveKey isKindOfClass:[NSString class]]) {
         for (ThemeItemModel * item in self.allThemes) {
             if ([item.themeId isEqualToString:saveKey]) {
                 _currentTheme = item.theme;
                 break;
            }
        }
    }

     /// 請求接口
   這里根據自己需求做
 }

②注冊回調

/注: 利用NSMapTable 去保存回調來達到對key的弱引用, 來確保已經釋放的類不會進行去調用回調/

- (void)regisObserver:(id)obj themeDidChangeCallBack:(void (^)(id<AWThemeBaseStyleProtocol> theme))callback {

     if (obj) {

         // 調用一次
         if (callback) {
             callback(_currentTheme);
        }

        [self.observers setObject:callback forKey:obj];
    }
 }
 
 - (NSMapTable *)observers {
     if (!_observers) {
         _observers = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemoryvalueOptions:NSPointerFunctionsCopyIn capacity:0];;
    }
     return _observers;
 }

③ 更換主題
 /// 更換皮膚
 - (void)changeTheme:(AWThemeItemModel *)themeItemModel {

     if (_currentTheme == themeItemModel.theme) {
         return;
    }

    [[NSUserDefaults standardUserDefaults] setObject:themeItemModel.themeIdforKey:ThemeSaveKey];
    [[NSUserDefaults standardUserDefaults] synchronize];

     _currentTheme = themeItemModel.theme;

     // 回調所有的注冊的回調
     if (self.observers.count) {
         NSEnumerator *enumerator = self.observers.objectEnumerator;
         void (^callback)(id<AWThemeBaseStyleProtocol> theme) = nil;
         while (callback = [enumerator nextObject]) {
             callback(_currentTheme);
        }
    }
 }

至此已完成主題相關操作, 接下來配置好數據之后, 業務倉向管理者注冊回調即可達到主題效果

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容

  • MVC: Model: 負責存儲,定義,操作數據;View: 用來展示給用戶數據,和用戶進行交互操作的;Contr...
    西風頌閱讀 750評論 4 20
  • 1.網絡 1.網絡七層協議有哪些? 物理層:主要功能:傳輸比特流;典型設備:集線器、中繼器;典型協議標準和應用:V...
    _我和你一樣閱讀 3,432評論 1 38
  • 1、APP的啟動過程、main函數? 內核初始化空間創建進程-》加載解析執行文件 - 》載入動態鏈接器(加載依賴庫...
    032c6843a285閱讀 770評論 2 12
  • iOS項目架構 做了幾個App,發現很多時候,App的基本框架都是一樣的,如何組織架構,讓項目更容易開發和維護,減...
    苦笑男神閱讀 1,799評論 8 9
  • 1、登錄(文本輸入、按鈕交互、基于網絡的交互) 2、刷新界面:(表視圖) 1>小部分應用程序數據來源于本地 2>更...
    炙冰閱讀 787評論 0 1