iOS标签栏动态化

iOS标签栏动态化_第1张图片

一、TabBar架构

RDVTabBarController开源地址

文件 说明
defauftTabBar.json 默认标签栏数据
MagicDownloader 标签栏item下载
MainTabBarManager 管理标签栏
MainTabBarModel 标签栏item模型
RDVTabBarController 项目中用到的根视图控制器
iOS标签栏动态化_第2张图片
1.1

二、说明

defauftTabBar.json文件

若未成功获取到服务器返回的TabBar配置数据,则默认使用本地defauftTabBar.json文件中的数据。

字段 配置说明
normal String 未点击状态url
focus String 已点击状态url
page String 需要显示的控制器
params String 自定义参数
track String 埋点、标识
(startTime) String 定时显示控制器
(endTime) String 定时隐藏控制器
[
  {
  "normal":"http://cdn1.showjoy.com/images/a3/a301101989e84950adf7a4c5b2a4f6c5.png",
  "focus":"http://cdn1.showjoy.com/images/ae/ae61cacb829d41268811b3c2d7cf9b86.png",
  "page":"HomeMainViewController",
  "params":"",
  "track":"tab_home"
  },
  {
  "normal":"http://cdn1.showjoy.com/images/d3/d3fed5dd8db3406bbd48e3931874af64.png",
  "focus":"http://cdn1.showjoy.com/images/7f/7f74216350c54eabaa8d2777b71294b7.png",
  "page":"ClassifyViewController",
  "params":"",
  "track":"tab_category"
  },
  {
  "normal":"http://cdn1.showjoy.com/images/5d/5d6feb9284d046619411c4fadd81720c.png",
  "focus":"http://cdn1.showjoy.com/images/50/50e00a05ac184e1b935bf63f37d8aee6.png",
  "page":"PersonViewController",
  "params":"",
  "track":"tab_user"
  }
]
MainTabBarModel文件

TabBar数据对应的Model模型

MainTabBarModel.h

#import 
@interface MainTabBarModel : NSObject
@property (nonatomic, copy) NSString  *focus;
@property (nonatomic, copy) NSString  *normal;
@property (nonatomic, copy) NSString  *page;
@property (nonatomic, copy) NSString  *params;
@property (nonatomic, copy) NSString  *track;
@property (nonatomic, copy) NSString  *startTime;
@property (nonatomic, copy) NSString  *endTime;
@end

MainTabBarModel.m

#import "MainTabBarModel.h"
@implementation MainTabBarModel
@end
MagicDownloader文件

GCD多线程处理,任务完成通过completion回调
1、根据TabBar数据解析的Model下载icon文件。
2、校验icon是否已经下载:

  • url和magic_local_url一致则根据magic_local_filePath获取本地icon文件。
  • url和magic_local_url不一致或magic_local_filePath获取本地icon文件失败,则网络下载icon文件。

3、icon文件保存到/Documents/路径下,NSUserDefaults持久化保存形式如下:

字段 说明
magic_local_filePath track值 + _normaltrack值 + _focus icon文件对应的本地路径
magic_local_url icon文件url icon文件对应的网络url

注意:
因为page字段配置的Controller名称不具备唯一性,所以使用具有唯一性的字段track + ...的形式来作为已经下载的icon名称。
replaceHttpToHttps方法作用是将url字符串的http协议转为https协议。

iOS标签栏动态化_第3张图片
Documents中保存的icon

MagicDownloader.h

#import 
#import "MainTabBarModel.h"

#define magic_tabbar_normal(key)       [NSString stringWithFormat:@"%@_normal", key]
#define magic_tabbar_focus(key)        [NSString stringWithFormat:@"%@_focus", key]

typedef void (^MagicDownloaderCompletion)(BOOL success);

@interface MagicDownloader : NSObject
+ (MagicDownloader *)shareManager;
- (void)startDownloadImageWithAllTabBarModels:(NSArray *)allTabBarModels Completion:(MagicDownloaderCompletion)completion;
- (UIImage *)sandboxOptionGetImageWithFileName:(NSString *)fileName;
@end

MagicDownloader.m

#import "MagicDownloader.h"

#define magic_local_url           @"local_url"
#define magic_local_filePath      @"local_filePath"

@implementation MagicDownloader

+ (MagicDownloader *)shareManager{
 
    static MagicDownloader *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [MagicDownloader new];
    });
    return manager;
    
}

#pragma mark - 多线程处理
- (void)startDownloadImageWithAllTabBarModels:(NSArray *)allTabBarModels Completion:(MagicDownloaderCompletion)completion{
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_group_t downloadImage = dispatch_group_create();
    
    [allTabBarModels enumerateObjectsUsingBlock:^(MainTabBarModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
        //NSLog(@"网络下载并保存到沙盒...");
        dispatch_group_async(downloadImage, queue, ^{
            
            NSString *key_normal = magic_tabbar_normal(model.track);
            NSString *key_focus = magic_tabbar_focus(model.track);
            [self checkSandBoxHaveImageWithFileName:key_normal url:model.normal];
            [self checkSandBoxHaveImageWithFileName:key_focus url:model.focus];
            
        });
    }];
    
    dispatch_group_notify(downloadImage, dispatch_get_main_queue(), ^{
        if (completion) {
            completion(YES);
        }
    });
    
}

#pragma mark - 检查图片是否下载
- (void)checkSandBoxHaveImageWithFileName:(NSString *)fileName url:(NSString *)url{
    
    UIImage *resultImage = nil;
    NSDictionary *local_dic = [self databaseCacheGetWithKey:fileName];
    NSString *local_url = [local_dic objectForKey:magic_local_url];
    NSString *local_filePath = [local_dic objectForKey:magic_local_filePath];

    //已下载 - 从本地获取
    if ([url isEqualToString:local_url]) {
        resultImage = [UIImage imageWithContentsOfFile:local_filePath];
    }
    if (resultImage == nil) {
        //未下载 — 网络下载
        resultImage = [self networkDownloadImageWithImageUrl:url];
    }
    //存储 - 图片
    if (resultImage) {
        [self databaseCacheSaveWithKey:fileName image:resultImage url:url];
    }
    
}

#pragma mark - 下载网络图片
- (UIImage *)networkDownloadImageWithImageUrl:(NSString *)imageUrl{
    
    if (!imageUrl.length) {
        return nil;
    }
    NSURL *url = [NSURL URLWithString:[imageUrl replaceHttpToHttps]];
    NSData *responseData = [NSData dataWithContentsOfURL:url];
    UIImage *resultImage = [UIImage imageWithData:responseData];
    return resultImage;
    
}
#pragma mark - 沙盒操作
/**
 存储图片
 */
- (NSString *)sandboxOptionSaveImage:(UIImage *)image fileName:(NSString *)fileName{
    
    NSString *homePath = NSHomeDirectory();
    NSString *imagePathName = [NSString stringWithFormat:@"/Documents/%@.png", fileName];
    NSString *filePath = [homePath stringByAppendingString:imagePathName];
    [UIImagePNGRepresentation(image) writeToFile:filePath atomically:YES];
    return [NSString stringWithFormat:@"%@", filePath];
    
}

/**
 获取图片
 */
- (UIImage *)sandboxOptionGetImageWithFileName:(NSString *)fileName{

    NSString *homePath = NSHomeDirectory();
    NSString *filePath = [homePath stringByAppendingString:[NSString stringWithFormat:@"/Documents/%@", fileName]];
    UIImage *image = [UIImage imageWithContentsOfFile:filePath];
    return image;
    
}

#pragma mark - 持久化存储操作
/**
 存储图片
 */
- (void)databaseCacheSaveWithKey:(NSString *)key image:(UIImage *)image url:(NSString *)url{
    
    NSString *filePath = [self sandboxOptionSaveImage:image fileName:key];
    NSDictionary *dic = @{magic_local_filePath : [NSString stringWithFormat:@"%@", filePath],
                          magic_local_url : [NSString stringWithFormat:@"%@", url]};
    [[NSUserDefaults standardUserDefaults] setObject:dic forKey:key];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
}

/**
 获取显示图片
 */
- (NSDictionary *)databaseCacheGetWithKey:(NSString *)key{
    
    NSDictionary *result = [[NSUserDefaults standardUserDefaults] objectForKey:key];
    return result;
    
}
MainTabBarManager文件

1、根据网络TabBar配置的json数据转为NSArray传递到dataSource中,生成一个根视图控制器,其中包含了需要显示的控制器。
2、page字段,用于生成对应的Controller。
3、params字段,用于WebViewController跳转URL。
4、startTime字段,用于定时显示Controller。
5、endTime字段,用于定时隐藏Controller。
6、NSUserDefaults持久化保存每次生成的TabBar数据,key为tabbar_lastDataSource,用于校验TabBar是否发生变化。

注意:
WeexViewController和ActivityWebViewController为业务中需要显示的控制器,这里不再详细说明。

MainTabBarManager.h

#import 
#import "RDVTabBarController.h"
@interface MainTabBarManager : NSObject
+ (MainTabBarManager *)shareManager;
// 加载根视图控制器
- (RDVTabBarController *)loadingMainTabBarControllerWithDataSource:(NSArray *)dataSource; 
// 获取上次TabBar数据
- (NSArray *)localGetLastDataSource;
// 清除上次TabBar数据
- (void)clearLocalGetLastDataSource;
@end

MainTabBarManager.m

#import "MainTabBarManager.h"
#import "RDVTabBarItem.h"
#import "MainTabBarModel.h"
#import "MagicDownloader.h"

//ViewControllers
#import "WeexViewController.h"
#import "ActivityWebViewController.h"

@interface MainTabBarManager ()
@property (nonatomic, strong)RDVTabBarController *rootTabBarController;
@end

@implementation MainTabBarManager

+ (MainTabBarManager *)shareManager{
    
    static MainTabBarManager *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [MainTabBarManager new];
    });
    return manager;
    
}


/**
 标签栏
 */
- (RDVTabBarController *)loadingMainTabBarControllerWithDataSource:(NSArray *)dataSource{
    
    [self localSaveLastDataSource:dataSource];
    self.rootTabBarController = nil;
    self.rootTabBarController = [RDVTabBarController new];
    self.rootTabBarController.tabBar.backgroundView.backgroundColor = [[OnlineManager shareOnlineManager] useOnlineTabbarBackGroundColor];
    self.rootTabBarController.viewControllers = [self buildAllViewControllersWithDataSource:dataSource];
    return self.rootTabBarController;
    
}

/**
 构建ViewController
 */
- (NSArray *)buildAllViewControllersWithDataSource:(NSArray *)dataSource{
    
    
    NSMutableArray *results = [NSMutableArray array];
    NSMutableArray *allModels = [NSMutableArray array];
    [dataSource enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        if ([obj isKindOfClass:[NSDictionary class]]) {
            MainTabBarModel *model = [MainTabBarModel objectWithKeyValues:obj];
            UIViewController *controller = nil;
            
            // 时间控制
            if ([self canAddRootViewControllerWithStartTime:model.startTime EndTime:model.endTime]) {
                if (model.params.length) {
                    // Weex 、WebView
                    controller = [self loadWeexOrWebViewControllerWithParams:model.params];
                }else{
                    // 普通
                    controller = [self loadNormalControllerWithPage:model.page];
                }
                if (controller) {
                    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
                    [results addObject:navigationController];
                    [allModels addObject:model];
                }
            }
            
        }
        
    }];
    
    //多线程加载
    [[MagicDownloader shareManager] startDownloadImageWithAllTabBarModels:allModels Completion:^(BOOL success) {
        [self reloadTabBarItemsWithAllModels:allModels];
    }];
    
    return results;
    
}


/**
 普通Controller
 */
- (UIViewController *)loadNormalControllerWithPage:(NSString *)page{
    
    Class cls = NSClassFromString(page);
    return (UIViewController *)[[cls alloc] init];
    
}

/**
 Weex或WebView
 */
- (UIViewController *)loadWeexOrWebViewControllerWithParams:(NSString *)params{
    
    CGRect frame = CGRectMake(0, 0, Screen_width, Screen_height - TabbarHeight - NavagationBarHeight);
    NSMutableDictionary *weexDic = [NSMutableDictionary dictionaryWithDictionary:[[JumpAgreement sharedJumpAgreement] getDicPushToWeexController:params]];
    if (kValidDic(weexDic) && [[weexDic objectForKey:@"url"] length] > 0) {
        //Weex
        [weexDic setValue:@"1" forKey:@"Main"];
        return [[WeexViewController alloc] initWithDict:weexDic withFrame:frame];
    }
    
    //WebView
    NSDictionary * mdicValue = [NSDictionary dictionaryWithObjectsAndKeys:params,@"url",@"1",@"Main", nil];
    return [[ActivityWebViewController alloc] initWithDict:mdicValue withFrame:frame];

}


/**
 刷新TabBarItem
 */
- (void)reloadTabBarItemsWithAllModels:(NSArray *)allModels{
    
    NSMutableArray *array = [NSMutableArray array];
    for (NSInteger i = 0; i < allModels.count; i++) {
        MainTabBarModel *model = [allModels objectAtIndex:i];
        RDVTabBarItem *item = [RDVTabBarItem new];
        item.title = nil;
        UIImage *normalImage = [[MagicDownloader shareManager] sandboxOptionGetImageWithFileName:magic_tabbar_normal(model.track)];
        UIImage *focusImage = [[MagicDownloader shareManager] sandboxOptionGetImageWithFileName:magic_tabbar_focus(model.track)];
        [item setFinishedSelectedImage:focusImage withFinishedUnselectedImage:normalImage];
        [array addObject:item];
        
    }
    self.rootTabBarController.tabBar.items = array;

}

#pragma mark - 持久化存储
- (void)localSaveLastDataSource:(NSArray *)dataSource{
    [[NSUserDefaults standardUserDefaults] setObject:dataSource forKey:@"tabbar_lastDataSource"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

/**
 上次数据源
 */
- (NSArray *)localGetLastDataSource{
    return [[NSUserDefaults standardUserDefaults] objectForKey:@"tabbar_lastDataSource"];
}

- (void)clearLocalGetLastDataSource{
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"tabbar_lastDataSource"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

#pragma mark - 时间控制
- (BOOL)canAddRootViewControllerWithStartTime:(NSString *)startTime EndTime:(NSString *)endTime{
    
    NSDate *startDate = [NSDate dateWithString:startTime formatString:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *endDate = [NSDate  dateWithString:endTime formatString:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *nowDate = [NSDate date];
    
    if (startTime.length <= 0 && endTime.length <= 0) {
        return YES;
    }
    
    if ([nowDate isLaterThanOrEqualTo:startDate] && [nowDate isEarlierThanOrEqualTo:endDate]) {
        return YES;
    }
    
    if ([nowDate isEarlierThan:startDate] && startTime.length) {
        return NO;
    }
    if ([nowDate isLaterThan:endDate] && endTime.length) {
        return NO;
    }
    return NO;
    
}
@end
RDVTabBarController文件

仅根据UI需求修改了源代码的item大小和TabBar的颜色,这里就不再过多说明。

iOS标签栏动态化_第4张图片
AppDelegate文件

在需要校验刷新刷新根视图控制器的地方调用reloadRootTabbarController方法

注意:
[[OnLineParameter getOnLineParameter:@"homeBar"] toArrayOrDictionary] 方法用于获取网络TagBar数据转为NSArray

/**
 刷新根视图控制器
 */
- (void)reloadRootTabbarController{
    
    NSArray *homeBarArray =  [[OnLineParameter getOnLineParameter:@"homeBar"] toArrayOrDictionary];
    NSArray *lastHomeBarArray = [[MainTabBarManager shareManager] localGetLastDataSource];
    
    if (homeBarArray.count <= 0 || [APPManager getIsCheck]) {
        //默认数据
        NSString *jsonString = [NSString stringWithContentsOfFile:Magic_bundle(@"defauftTabBar", @"json") encoding:NSUTF8StringEncoding error:nil];
        NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
        homeBarArray = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:nil];
    }
    
    if (![homeBarArray isEqualToArray:lastHomeBarArray]) {
        //网络数据
        self.viewController = [[MainTabBarManager shareManager] loadingMainTabBarControllerWithDataSource:homeBarArray];
        self.window.rootViewController = self.viewController;
        return;
    }

    if (self.viewController == nil) {
        //防止空视图
        self.viewController = [[MainTabBarManager shareManager] loadingMainTabBarControllerWithDataSource:homeBarArray];
        self.window.rootViewController = self.viewController;
        return;
    }
    
}

三、使用

动态显示标签栏,通过后台网络配置TabBar数据实现,示例如下:

[
    {
        "normal":"http://cdn1.showjoy.com/images/a3/a301101989e84950adf7a4c5b2a4f6c5.png",
        "focus":"http://cdn1.showjoy.com/images/ae/ae61cacb829d41268811b3c2d7cf9b86.png",
        "page":"HomeMainViewController",
        "params":"",
        "track":"tab_home"
    },
    {
        "normal":"http://cdn1.showjoy.com/images/d3/d3fed5dd8db3406bbd48e3931874af64.png",
        "focus":"http://cdn1.showjoy.com/images/7f/7f74216350c54eabaa8d2777b71294b7.png",
        "page":"ClassifyViewController",
        "params":"",
        "track":"tab_category"
    },
   {
        "normal":"https://cdn1.showjoy.com/images/ee/ee993cdac0c540a0a3687853623c7d07.png",
        "focus":"https://cdn1.showjoy.com/images/ee/ee993cdac0c540a0a3687853623c7d07.png",
        "page": "MainActivityController",
        "startTime":"2017-07-09 10:00:00",
        "endTime":"2017-07-14 23:59:59",
        "params":"https://shop.m.showjoy.com/activity/shop/22.html",
        "track": "tab_activity"
    },
    {
        "normal":"http://cdn1.showjoy.com/images/0c/0c252712186a46d2a06ac2acd95ecdc6.png",
        "focus":"http://cdn1.showjoy.com/images/12/12792c6085f04136829763610d31ba5f.png",
        "page":"MainActivityController",
        "params":"http://shop.m.showjoy.com/shop/activityExposure/hotHomeWeex.html",
        "track":"tab_hotHome"
    },
    {
        "normal":"http://cdn1.showjoy.com/images/5d/5d6feb9284d046619411c4fadd81720c.png",
        "focus":"http://cdn1.showjoy.com/images/50/50e00a05ac184e1b935bf63f37d8aee6.png",
        "page":"PersonViewController",
        "params":"",
        "track":"tab_user"
    }
]
iOS标签栏动态化_第5张图片
Demo

Demo整理中。。。

你可能感兴趣的:(iOS标签栏动态化)