一、TabBar架構(gòu)
文件 | 說(shuō)明 |
---|---|
defauftTabBar.json | 默認(rèn)標(biāo)簽欄數(shù)據(jù) |
MagicDownloader | 標(biāo)簽欄item下載 |
MainTabBarManager | 管理標(biāo)簽欄 |
MainTabBarModel | 標(biāo)簽欄item模型 |
RDVTabBarController | 項(xiàng)目中用到的根視圖控制器 |
二策州、說(shuō)明
defauftTabBar.json文件
若未成功獲取到服務(wù)器返回的TabBar配置數(shù)據(jù),則默認(rèn)使用本地defauftTabBar.json文件中的數(shù)據(jù)宫仗。
字段 | 值 | 配置說(shuō)明 |
---|---|---|
normal | String | 未點(diǎn)擊狀態(tài)url |
focus | String | 已點(diǎn)擊狀態(tài)url |
page | String | 需要顯示的控制器 |
params | String | 自定義參數(shù) |
track | String | 埋點(diǎn)够挂、標(biāo)識(shí) |
(startTime) | String | 定時(shí)顯示控制器 |
(endTime) | String | 定時(shí)隱藏控制器 |
[
{
"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數(shù)據(jù)對(duì)應(yīng)的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多線程處理,任務(wù)完成通過(guò)completion回調(diào)
1藕夫、根據(jù)TabBar數(shù)據(jù)解析的Model下載icon文件下硕。
2丁逝、校驗(yàn)icon是否已經(jīng)下載:
- url和magic_local_url一致則根據(jù)magic_local_filePath獲取本地icon文件。
- url和magic_local_url不一致或magic_local_filePath獲取本地icon文件失敗梭姓,則網(wǎng)絡(luò)下載icon文件霜幼。
3、icon文件保存到/Documents/路徑下誉尖,NSUserDefaults持久化保存形式如下:
字段 | 值 | 說(shuō)明 |
---|---|---|
magic_local_filePath |
track值 + _normal 或track值 + _focus
|
icon文件對(duì)應(yīng)的本地路徑 |
magic_local_url | icon文件url | icon文件對(duì)應(yīng)的網(wǎng)絡(luò)url |
注意:
因?yàn)閜age字段配置的Controller名稱不具備唯一性罪既,所以使用具有唯一性的字段track + ...
的形式來(lái)作為已經(jīng)下載的icon名稱。
replaceHttpToHttps方法作用是將url字符串的http協(xié)議轉(zhuǎn)為https協(xié)議铡恕。
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(@"網(wǎng)絡(luò)下載并保存到沙盒...");
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) {
//未下載 — 網(wǎng)絡(luò)下載
resultImage = [self networkDownloadImageWithImageUrl:url];
}
//存儲(chǔ) - 圖片
if (resultImage) {
[self databaseCacheSaveWithKey:fileName image:resultImage url:url];
}
}
#pragma mark - 下載網(wǎng)絡(luò)圖片
- (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 - 沙盒操作
/**
存儲(chǔ)圖片
*/
- (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 - 持久化存儲(chǔ)操作
/**
存儲(chǔ)圖片
*/
- (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琢感、根據(jù)網(wǎng)絡(luò)TabBar配置的json數(shù)據(jù)轉(zhuǎn)為NSArray傳遞到dataSource中,生成一個(gè)根視圖控制器探熔,其中包含了需要顯示的控制器驹针。
2、page字段诀艰,用于生成對(duì)應(yīng)的Controller柬甥。
3、params字段其垄,用于WebViewController跳轉(zhuǎn)URL苛蒲。
4、startTime字段绿满,用于定時(shí)顯示Controller臂外。
5、endTime字段喇颁,用于定時(shí)隱藏Controller漏健。
6、NSUserDefaults持久化保存每次生成的TabBar數(shù)據(jù)橘霎,key為tabbar_lastDataSource漾肮,用于校驗(yàn)TabBar是否發(fā)生變化。
注意:
WeexViewController和ActivityWebViewController為業(yè)務(wù)中需要顯示的控制器茎毁,這里不再詳細(xì)說(shuō)明克懊。
MainTabBarManager.h
#import <Foundation/Foundation.h>
#import "RDVTabBarController.h"
@interface MainTabBarManager : NSObject
+ (MainTabBarManager *)shareManager;
// 加載根視圖控制器
- (RDVTabBarController *)loadingMainTabBarControllerWithDataSource:(NSArray *)dataSource;
// 獲取上次TabBar數(shù)據(jù)
- (NSArray *)localGetLastDataSource;
// 清除上次TabBar數(shù)據(jù)
- (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;
}
/**
標(biāo)簽欄
*/
- (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;
}
/**
構(gòu)建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;
// 時(shí)間控制
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 - 持久化存儲(chǔ)
- (void)localSaveLastDataSource:(NSArray *)dataSource{
[[NSUserDefaults standardUserDefaults] setObject:dataSource forKey:@"tabbar_lastDataSource"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
/**
上次數(shù)據(jù)源
*/
- (NSArray *)localGetLastDataSource{
return [[NSUserDefaults standardUserDefaults] objectForKey:@"tabbar_lastDataSource"];
}
- (void)clearLocalGetLastDataSource{
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"tabbar_lastDataSource"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark - 時(shí)間控制
- (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文件
僅根據(jù)UI需求修改了源代碼的item大小和TabBar的顏色七蜘,這里就不再過(guò)多說(shuō)明谭溉。
AppDelegate文件
在需要校驗(yàn)刷新刷新根視圖控制器的地方調(diào)用
reloadRootTabbarController
方法注意:
[[OnLineParameter getOnLineParameter:@"homeBar"] toArrayOrDictionary]
方法用于獲取網(wǎng)絡(luò)TagBar數(shù)據(jù)轉(zhuǎn)為NSArray
/**
刷新根視圖控制器
*/
- (void)reloadRootTabbarController{
NSArray *homeBarArray = [[OnLineParameter getOnLineParameter:@"homeBar"] toArrayOrDictionary];
NSArray *lastHomeBarArray = [[MainTabBarManager shareManager] localGetLastDataSource];
if (homeBarArray.count <= 0 || [APPManager getIsCheck]) {
//默認(rèn)數(shù)據(jù)
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]) {
//網(wǎng)絡(luò)數(shù)據(jù)
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;
}
}
三、使用
動(dòng)態(tài)顯示標(biāo)簽欄橡卤,通過(guò)后臺(tái)網(wǎng)絡(luò)配置TabBar數(shù)據(jù)實(shí)現(xiàn)扮念,示例如下:
[
{
"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"
}
]