iOS主題設(shè)計

iOS主題

第一種方案思路:

業(yè)務(wù)方:
  • 添加監(jiān)聽主題變更通知, 在通知方法里面進行重置主題
主題管理者業(yè)務(wù):
  • 主題的配置
  • 發(fā)送主題變更通知

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

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

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

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

合并一下前兩種方案

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

這樣之后的方案就是

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

代碼思路圖示

image-20211214102528845.png

由上圖導出下圖具體代碼

image-20211214102517079.png

代碼講解:

數(shù)據(jù)協(xié)議

基礎(chǔ)主題協(xié)議: 這是定義業(yè)務(wù)使用主題的字段
 @protocol ThemeBaseStyleProtocol <NSObject>
 
 /// tabbar的背景顏色
 @property (nonatomic , strong) UIColor *tabbar_bg_color;
 /// navigation背景顏色
 @property (nonatomic , strong) UIColor *navigation_bg_color;
 /// tabbar未讀數(shù)的背景顏色
 @property (nonatomic , strong) UIColor *tabbar_unread_num_bg_color;
 /// tabbar未讀數(shù)的文案顏色
 @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;
 /// 頂部購物車數(shù)值顏色
 @property (nonatomic , strong) UIColor *mall_navigation_shoping_num_color;
 /// 頂部購物車數(shù)值背景顏色
 @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;
 
 /// 預(yù)留給網(wǎng)絡(luò)數(shù)據(jù)轉(zhuǎn)化為本地數(shù)據(jù)
 - (instancetype)initWithThemeConfig:(NSDictionary *)config;
 
 @end
主題擴展協(xié)議: 這里是為了給主題配置一個包裝層, 供擴展用,例如遠程數(shù)據(jù)和本地數(shù)據(jù)的相互兼容
@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

管理者

管理者協(xié)議
  /// 主題
 @protocol ThemeProtocol <NSObject>
 
 /// 當前所有的皮膚
 @property (nonatomic , strong) NSMutableArray <ThemeItemModel *> *allThemes;
 
 /// 當前的主題, 默認: ThemeDetaultStyle
 @property (nonatomic , strong, readonly) id<ThemeBaseStyleProtocol>currentTheme;
 
 /// 注冊主題改變之后的回調(diào)
 /// 注冊可以采用分類方法, 寫了快捷方式
 /// 想要刪除回調(diào),可設(shè)置回調(diào)為nil即可
 /// 不會對obj進行強引用, 外界obj釋放掉之后,里面對應(yīng)的回調(diào)也會銷毀
 /// 內(nèi)部會先自動調(diào)用一次這個回調(diào)
 /// @param obj 響應(yīng)者
 /// @param callback 回調(diào)
 - (void)regisObserver:(id _Nonnull)obj themeDidChangeCallBack:(void(^ _Nullable)(id<ThemeBaseStyleProtocol> theme))callback;
 
 /// 更新皮膚配置, 從后臺獲取是否有新的皮膚, 以保存到本地區(qū)使用, 本地預(yù)存了兩份
 - (void)updateConfigTheme;
 
 /// 更換皮膚
 - (void)changeTheme:(ThemeItemModel *)themeItemModel;
 
 @end
快捷業(yè)務(wù)方法
@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 = 獲取到實現(xiàn)ThemeProtocol 協(xié)議的實現(xiàn)者, 必須單類;

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

     id <ThemeProtocol>theme = 獲取到實現(xiàn)ThemeProtocol 協(xié)議的實現(xiàn)者, 必須單類;

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

生成協(xié)議之后,基本上數(shù)據(jù)流已經(jīng)通了

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

管理者實現(xiàn)代碼

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

    [self.allThemes removeAllObjects];
     // 默認
     ThemeItemModel *defaultModel = [ThemeItemModel new];
     defaultModel.title = @"默認";
     defaultModel.themeId = @"18c63459a2c069022c7790430f761214";// 默認 MD5
   //ThemeDetaultStyle 這個就是實現(xiàn)了主題基礎(chǔ)協(xié)議的數(shù)據(jù)類,返回了主題的顏色和圖片
     defaultModel.theme = [ThemeDetaultStyle new];
    [self.allThemes addObject:defaultModel];
     _currentTheme = defaultModel.theme;
     // 新年
     ThemeItemModel *yearModel = [ThemeItemModel new];
     yearModel.title = @"新年";
     yearModel.themeId = @"d46d5bb15db17203e4e5d61372325f91";// 新年 MD5
   //ThemeNewYearStyle 這個就是實現(xiàn)了主題基礎(chǔ)協(xié)議的數(shù)據(jù)類,返回了主題的顏色和圖片
     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;
            }
        }
    }

     /// 請求接口
   這里根據(jù)自己需求做
 }

②注冊回調(diào)

/注: 利用NSMapTable 去保存回調(diào)來達到對key的弱引用, 來確保已經(jīng)釋放的類不會進行去調(diào)用回調(diào)/

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

     if (obj) {

         // 調(diào)用一次
         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;

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

至此已完成主題相關(guān)操作, 接下來配置好數(shù)據(jù)之后, 業(yè)務(wù)倉向管理者注冊回調(diào)即可達到主題效果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容