iOS主题设计

2021-12-14  本文已影响0人  kalpa_shock

iOS主题

第一种方案思路:

业务方:
主题管理者业务:

第二种方案思路
业务方:
主题管理者业务:

第三种方案( 不建议 )
直接替换主工程window的root控制器,直接全部重新创建

优缺点分析

合并一下前两种方案

采用第一种管理层做法: 由管理层去管理主题的配置, 并增加第二种管理层的服务: 注册回调功能

这样之后的方案就是

业务方:
主题管理层:
这样之后业务方使用方便, 组件化采用协议注册形式达到解耦主题管理者和业务

代码思路图示

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);
        }
    }
 }

至此已完成主题相关操作, 接下来配置好数据之后, 业务仓向管理者注册回调即可达到主题效果

上一篇下一篇

猜你喜欢

热点阅读