最近公司讓我們把APP的tabbar按鈕寫活,什么意思呢?就是讀取后臺(tái)數(shù)據(jù),后臺(tái)讓你把哪個(gè)controller設(shè)置成tabbar,你就得在本地把哪個(gè)controller寫成tabbar。總結(jié)為一句話:讀取后臺(tái)數(shù)據(jù),設(shè)置tabbar。是不是看到這里懵逼了。是的當(dāng)我聽到這個(gè)消息的時(shí)候我也懵逼了。因?yàn)槲宜娺^的所有用原生寫的主流app,人家的tabbar都是寫死的。但是老大說了,客戶有這樣的需求,必須實(shí)現(xiàn)。我只能硬著頭皮去嘗試了,而且我們的app里還有抽屜,抽屜和自定義的tabbar放在一起,可想而知會(huì)炸了,果然這東西花了我半個(gè)月時(shí)間。言歸正傳,下面我來介紹,如何根據(jù)后臺(tái)數(shù)據(jù)寫你的tabbar。
對了提一個(gè)小建議:做項(xiàng)目的時(shí)候 最好建一個(gè) 基類,創(chuàng)建其他的控制器繼承這個(gè)基類 ,基類很好用,也很方便。
1.遇到的問題:根據(jù)后臺(tái)數(shù)據(jù)設(shè)定tabbar 那么這個(gè)數(shù)據(jù)請求肯定要寫在APPdelegate 的 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法里 。只有拿到數(shù)據(jù)我們才能去設(shè)置tabbar? 而 根視圖 要設(shè)置成第一個(gè)tabbar的controller? 且必須在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法里設(shè)定,不在該方法設(shè)定會(huì)報(bào)錯(cuò)。 那數(shù)據(jù)請求(我用的是ASI)是異步的,所以肯定會(huì)報(bào)錯(cuò)了。
解決的方法是:在
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {? }
方法里先設(shè)置一個(gè)空白的controller做為根視圖,等到數(shù)據(jù)請求完成之后,根據(jù)后臺(tái)數(shù)據(jù)設(shè)置tabbar ,再把第一個(gè)tabbar重新設(shè)置成根視圖(注意:寫在主線程里)。如果后臺(tái)給的數(shù)據(jù)沒有tabbar,那就把抽屜的第一條數(shù)據(jù)所對應(yīng)的controller設(shè)置成根視圖(這時(shí)候只有抽屜沒有tabbar)。至于如何判斷tabbar,那需要你和后臺(tái)約定好字段,根據(jù)約定的字段去判斷了,代碼:
解釋: myfri就是 你判斷后,得到要寫成tabbar的conroller
UIViewController *conTroll = myfri;
UINavigationController *mainNAV = [[UINavigationController alloc] initWithRootViewController:conTroll];
//存 未選中
NSString *myStr = [NSString stringWithFormat:@"%@",[dicc objectForKey:@"app_btn_icon"]];
NSString *str = [NSString stringWithFormat:@"tabbarIma%d@2x.png",i];
[self mySaveImage:myStr andNumber:str];
NSString *fullPath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:str];
//存 選中
NSString *myStr1 = [NSString stringWithFormat:@"%@",[dicc objectForKey:@"app_btn_click_icon"]];
NSString *str1 = [NSString stringWithFormat:@"tabbarImano%d@2x.png",i];
NSString *fullPath1 = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:str1];
[self mySaveImage:myStr1 andNumber:str1];
//取
UIImage *savedImage = [[UIImage alloc] initWithContentsOfFile:fullPath];
UIImage *savedImage1 = [[UIImage alloc] initWithContentsOfFile:fullPath1];
// tabbar 圖片賦值
mainNAV.tabBarItem.selectedImage = [savedImage1 imageWithRenderingMode:UIImageRenderingModeAutomatic];
mainNAV.tabBarItem.image = [savedImage imageWithRenderingMode:UIImageRenderingModeAutomatic];
NSLog(@"1111的%@",savedImage);
//tabbar 字體大小
[mainNAV.tabBarItem setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:@"Helvetica" size:12.0], NSFontAttributeName, nil]
forState:UIControlStateNormal];
mainNAV.tabBarItem.title = [dicc objectForKey:@"app_btn_name"];
mainNAV.tabBarItem.tag = 2222+i ;
[arrtab addObject:mainNAV];
// 我自己定義的tabbar
XTabbarViewController *tabbar = [[XTabbarViewController alloc]init];
tabbar.tabBar.tintColor = UIColorFromRGBA(0xe13836);
tabbar.viewControllers = arrtab;
self.sideViewController = [[YRSideViewController alloc] init];? // YRSideViewController 是抽屜的三方 自己百度下載 這里我把它定義成了屬性
// 用于 第一個(gè)tabbar 的數(shù)據(jù)切換 起始值為 0 表示下面的第一個(gè)tabbr 顯示tabbar自己的數(shù)據(jù),為 1 時(shí)顯示左側(cè)第一個(gè)欄目數(shù)據(jù)
[[NSUserDefaults standardUserDefaults] setObject:@"0" forKey:@"cutTabbar"]; //這個(gè)在左側(cè)第一欄和tabbar之間切換時(shí)用
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self.sideViewController];
self.sideViewController.leftViewShowWidth = Width * 2.0 / 5.0 ;
self.sideViewController.needSwipeShowMenu = NO;
nav.navigationBarHidden = YES;
self.sideViewController.rightViewShowWidth = 300;
self.sideViewController.rootViewController = tabbar; //設(shè)置抽屜的根試圖
self.sideViewController.leftViewController = leftVC; //設(shè)置 左側(cè)抽屜的controller?? leftVC里面寫的是tableview 抽屜上一欄一欄的其實(shí)是這個(gè)tableview的cell
self.sideViewController.needSwipeShowMenu = NO;//默認(rèn)開啟的可滑動(dòng)展示
self.window.rootViewController = nav;;? //把上面的nav設(shè)置成根視圖
2.遇到的問題: 假如第一個(gè)tabbar對應(yīng)的controller 和抽屜的第一欄所對應(yīng)的controller不一樣,那么抽屜的第一欄所對應(yīng)的controller是無法顯示的, 也就是說你點(diǎn)擊左側(cè)抽屜第一欄,永遠(yuǎn)顯示的是第一個(gè)tabbar所對應(yīng)的controller? 這個(gè)問題困惑了我好久,一直找不到解決的辦法,最后試了各種方法最終解決了。
解決的方法:創(chuàng)建一個(gè)controller做為父視圖控制器,把它作為第一個(gè)tabbar?? 把左側(cè)第一欄所對應(yīng)的controller 和 第一個(gè)tabbar所對應(yīng)的controller 作為子視圖控制器 添加到父視圖控制器上 代碼:
[self addChildViewController:self.tabbarVC]; //self.tabbarVC 表示的tabbar的controller
[self.view addSubview:self.tabbarVC.view];
self.currentVC = self.tabbarVC;? // self.currentVC 表示的是當(dāng)前顯示的controller
然后? 在抽屜的第一欄的點(diǎn)擊事件- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// 點(diǎn)擊左側(cè)第一個(gè) 欄目? 1 表示 切換下面的第一個(gè)tabbar
[[NSUserDefaults standardUserDefaults] setObject:@"1" forKey:@"cutTabbar"];
[userDefaults synchronize];
調(diào)用 [sideViewController hideSideViewController:YES];?? 方法
}
在抽屜三方的 - (void)hideSideViewController:(BOOL)animated{?? “發(fā)通知 ”};
發(fā)消息通知?? 父視圖控制器接收到通知 就去判斷 父視圖當(dāng)前顯示的視圖是不是左側(cè)的 controller 不是就切換成左側(cè)controller 。
那如何切回原來tabbar的 controller呢? 很簡單,同樣在我們自定義的XTabbarViewController類的 的點(diǎn)擊事件里 發(fā)消息通知? 父視圖控制器接收到通知 判斷當(dāng)前顯示的controller 是不是tabbar 對應(yīng)的controller ,不是就切換。父視圖 消息中心執(zhí)行代碼:
-(void)myNotification:(NSNotification *)not
{
NSLog(@"childViewControllers === %@",self.childViewControllers);
NSDictionary *ddd =not.object;
NSLog(@"取出 幾 ? %@",not.object);
//0 代表 點(diǎn)擊的是tabbar 1代表點(diǎn)擊的是 左側(cè)的欄目
if ([ddd[@"key"] integerValue] == 0) {
NSLog(@"self.leftType == %@",self.leftType);
NSString * childType = [NSString stringWithFormat:@"%@",[self.childViewControllers[0] class]] ;
if ([childType isEqualToString:self.leftType]) {
self.tabBarController.tabBar.tintColor = UIColorFromRGBA(0xe13836);; //切換成tabbar的顏色
[self replaceController:self.leftVC newController:self.tabbarVC];
}else{
}
}else if([ddd[@"key"] integerValue] == 1){
NSString *nub =? [[NSUserDefaults standardUserDefaults] objectForKey:@"cutTabbar"];
NSLog(@"cutTabbar == %@",nub);
if ([nub integerValue] == 1) {
NSString * childType = [NSString stringWithFormat:@"%@",[self.childViewControllers[0] class]] ;
self.tabBarController.tabBar.tintColor = UIColorFromRGBA(0x999999); //顯示的是左側(cè)抽屜controller 切換成回顏色
if ([childType isEqualToString:self.tabbarType]) {
[self replaceController:self.tabbarVC newController:self.leftVC];
}else{
NSLog(@"不一樣");
}
// 切換了第一個(gè)tabbar的數(shù)據(jù)后 重置這個(gè)值 否則點(diǎn)擊回到左側(cè)抽屜時(shí),即便不點(diǎn)擊左側(cè)第一個(gè)欄目也會(huì)切換成左側(cè)第一個(gè)欄目
[[NSUserDefaults standardUserDefaults] setObject:@"0" forKey:@"cutTabbar"];
}
}else{
self.tabBarController.tabBar.tintColor = UIColorFromRGBA(0xe13836);
}
}
//? 切換各個(gè)標(biāo)簽內(nèi)容
- (void)replaceController:(UIViewController *)oldController newController:(UIViewController *)newController
{
/**
*??????????? 著重介紹一下它
*? transitionFromViewController:toViewController:duration:options:animations:completion:
*? fromViewController????? 當(dāng)前顯示在父視圖控制器中的子視圖控制器
*? toViewController??????? 將要顯示的姿勢圖控制器
*? duration??????????????? 動(dòng)畫時(shí)間(這個(gè)屬性,old friend 了 O(∩_∩)O)
*? options???????????????? 動(dòng)畫效果(漸變,從下往上等等,具體查看API)
*? animations????????????? 轉(zhuǎn)換過程中得動(dòng)畫
*? completion????????????? 轉(zhuǎn)換完成
*/
if ([newController isKindOfClass:[oldController class]] ) {
}else{
[self addChildViewController:newController];
[self transitionFromViewController:oldController toViewController:newController duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:^(BOOL finished) {
if (finished) {
[newController didMoveToParentViewController:self];
[oldController willMoveToParentViewController:nil];
[oldController removeFromParentViewController];
self.currentVC = newController;
}else{
self.currentVC = oldController;
}
}];
}
}
3.遇到的問題:? 我們都知道tabbar的圖片設(shè)置是 在本地放幾張圖片,名字要寫成“xxx@2.png”?? 用 [UIImage imageNamed:] 去設(shè)置,為什么要把名字寫成這樣呢?自己百度一下。然而我們的tabbar根據(jù)后臺(tái)設(shè)定了,那他的圖片不可能放到我們本地吧,所以要用后臺(tái)給的圖片網(wǎng)址去設(shè)置了。這個(gè)問題也是搞了好久,我用【UIImage imageWithData:】,然后處理了一下大小? 圖片是可以顯示上去,但是一直很模糊。最后想到用沙盒存儲(chǔ),終于解決了這個(gè)問題 。存的 代碼:
NSString *str = [NSString stringWithFormat:@"tabbarIma%d@2x.png",i];// i表示的是第幾個(gè)tabbar
NSString *fullPath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]stringByAppendingPathComponent:str];
BOOL haveImageFile =? [Tools toolsOfHasLibraryPathName:fullPath];
NSLog(@"fullPath == %@?? 本地有路徑嗎 = %hhd",fullPath,haveImageFile);
if (!haveImageFile) {
[self mySaveImage:myStr andNumber:str];
}
-(void)mySaveImage:(NSString *)urlString andNumber:(NSString *)strnum
{
//??? NSString *urlString = @"http://rmt.oss-cn-hangzhou.aliyuncs.com/public/icontest02.png";
NSData *data = [NSData dataWithContentsOfURL:[NSURL? URLWithString:urlString]];
UIImage *image = [UIImage imageWithData:data]; // 取得圖片
// 本地沙盒目錄
// 得到本地沙盒中名為"MyImage"的路徑,"MyImage"是保存的圖片名
//??? NSString *strnum = [NSString stringWithFormat:@"%@@2x.png",number];
NSString *imageFilePath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:strnum];
// 將取得的圖片寫入本地的沙盒中,其中0.5表示壓縮比例,1表示不壓縮,數(shù)值越小壓縮比例越大
NSLog(@"圖片路徑 == %@",imageFilePath);
BOOL success = [UIImagePNGRepresentation(image) writeToFile:imageFilePath? atomically:YES];
if (success){
NSLog(@"寫入本地成功");
}
}
取圖片的代碼:
UIImage *savedImage = [[UIImage alloc] initWithContentsOfFile:fullPath]; //未選中圖片
UIImage *savedImage1 = [[UIImage alloc] initWithContentsOfFile:fullPath1]; //選中圖片
mainNAV.tabBarItem.selectedImage = [savedImage1? imageWithRenderingMode:UIImageRenderingModeAutomatic];
mainNAV.tabBarItem.image =? [savedImage imageWithRenderingMode:UIImageRenderingModeAutomatic];
其實(shí)利用沙盒 ,無非就是為了存儲(chǔ)的時(shí)候可以給圖片 賦一個(gè)"xxx@2x"的名字
4.遇到的問題: 消息推送的跳轉(zhuǎn)
以前寫死的tabbar的app里? 假如你把消息推送的跳轉(zhuǎn)寫在 第一個(gè)tabbar的controller上 ,那你的當(dāng)前界面如果在其他tabbar里收到了通知,你點(diǎn)擊查看,是不是跳轉(zhuǎn)不過去,除非你在點(diǎn)擊事件里改變tabbar的索引為0。 那我們的tabbar都寫活了,你都不知道哪個(gè)controller要被設(shè)置成第一個(gè)tabbar,所以這種方法無法解決 。找了一些資料 看了看,發(fā)現(xiàn)基類可以解決,而且都不需要去改變tabbar的索引,試了一下果然可以。我頓時(shí)對基類這個(gè)東西刮目相看了。
解決的方法:
在基類里 寫:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationToNewsDetail:) name:@"skipToNewsDetail" object:nil];
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"skipToNewsDetail" object:nil];
}
- (void)notificationToNewsDetail:(NSNotification *)noti
{
//??? [self tabbarchange];
NSMutableDictionary *dic =(NSMutableDictionary *)noti.userInfo;
NSString *myKey =[NSString stringWithFormat:@"%@",[ dic? objectForKey:@"info_key"]];
NSLog(@"推送過來的,東西%@",dic);
if (STRING_ISNIL(myKey)) {
}
else{
NewsJSViewController? * newsVC = [[NewsJSViewController? alloc]init];
newsVC.keyString = myKey;
newsVC.typeString=[NSString stringWithFormat:@"%@",[dic objectForKey:@"info_class"]];
newsVC.dictionary=dic;
newsVC.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:newsVC animated:YES];
}
}
發(fā)通知的地方 當(dāng)然是appdelegate 的
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
userInfo 里面是推送過來的數(shù)據(jù) 你可以在這里寫一個(gè) alertController? 在他的確定時(shí)間里 寫你的發(fā)送通知的代碼。
}
5. 遇到的問題 里面還會(huì)遇到一些小的問題,比如每一個(gè)controller的 布局問題, 如果這個(gè)controller被設(shè)置成tabbar了 那么它里面的 tableview或者collectionView 的fram 的高度是不是 要減去49 。這個(gè)需要在controller里做判斷的。等等一些小問題 就不一一列舉了。****