一、TabBar架构
文件 | 说明 |
---|---|
defauftTabBar.json | 默认标签栏数据 |
MagicDownloader | 标签栏item下载 |
MainTabBarManager | 管理标签栏 |
MainTabBarModel | 标签栏item模型 |
RDVTabBarController | 项目中用到的根视图控制器 |
二、说明
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 <Foundation/Foundation.h>
@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值 + _normal 或track值 + _focus
|
icon文件对应的本地路径 |
magic_local_url | icon文件url | icon文件对应的网络url |
注意:
因为page字段配置的Controller名称不具备唯一性,所以使用具有唯一性的字段track + ...
的形式来作为已经下载的icon名称。
replaceHttpToHttps方法作用是将url字符串的http协议转为https协议。
MagicDownloader.h
#import <Foundation/Foundation.h>
#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 <Foundation/Foundation.h>
#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的颜色,这里就不再过多说明。
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"
}
]