最近公司讓我們把APP的tabbar按鈕寫活,什么意思呢?就是讀取后臺數據,后臺讓你把哪個controller設置成tabbar,你就得在本地把哪個controller寫成tabbar。總結為一句話:讀取后臺數據,設置tabbar。是不是看到這里懵逼了。是的當我聽到這個消息的時候我也懵逼了。因為我所見過的所有用原生寫的主流app,人家的tabbar都是寫死的。但是老大說了,客戶有這樣的需求,必須實現。我只能硬著頭皮去嘗試了,而且我們的app里還有抽屜,抽屜和自定義的tabbar放在一起,可想而知會炸了,果然這東西花了我半個月時間。言歸正傳,下面我來介紹,如何根據后臺數據寫你的tabbar。
對了提一個小建議:做項目的時候 最好建一個 基類,創建其他的控制器繼承這個基類 ,基類很好用,也很方便。
1.遇到的問題:根據后臺數據設定tabbar 那么這個數據請求肯定要寫在APPdelegate 的 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法里 。只有拿到數據我們才能去設置tabbar? 而 根視圖 要設置成第一個tabbar的controller? 且必須在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法里設定,不在該方法設定會報錯。 那數據請求(我用的是ASI)是異步的,所以肯定會報錯了。
解決的方法是:在
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {? }
方法里先設置一個空白的controller做為根視圖,等到數據請求完成之后,根據后臺數據設置tabbar ,再把第一個tabbar重新設置成根視圖(注意:寫在主線程里)。如果后臺給的數據沒有tabbar,那就把抽屜的第一條數據所對應的controller設置成根視圖(這時候只有抽屜沒有tabbar)。至于如何判斷tabbar,那需要你和后臺約定好字段,根據約定的字段去判斷了,代碼:
解釋: 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 是抽屜的三方 自己百度下載 這里我把它定義成了屬性
// 用于 第一個tabbar 的數據切換 起始值為 0 表示下面的第一個tabbr 顯示tabbar自己的數據,為 1 時顯示左側第一個欄目數據
[[NSUserDefaults standardUserDefaults] setObject:@"0" forKey:@"cutTabbar"]; //這個在左側第一欄和tabbar之間切換時用
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; //設置抽屜的根試圖
self.sideViewController.leftViewController = leftVC; //設置 左側抽屜的controller?? leftVC里面寫的是tableview 抽屜上一欄一欄的其實是這個tableview的cell
self.sideViewController.needSwipeShowMenu = NO;//默認開啟的可滑動展示
self.window.rootViewController = nav;;? //把上面的nav設置成根視圖
2.遇到的問題: 假如第一個tabbar對應的controller 和抽屜的第一欄所對應的controller不一樣,那么抽屜的第一欄所對應的controller是無法顯示的, 也就是說你點擊左側抽屜第一欄,永遠顯示的是第一個tabbar所對應的controller? 這個問題困惑了我好久,一直找不到解決的辦法,最后試了各種方法最終解決了。
解決的方法:創建一個controller做為父視圖控制器,把它作為第一個tabbar?? 把左側第一欄所對應的controller 和 第一個tabbar所對應的controller 作為子視圖控制器 添加到父視圖控制器上 代碼:
[self addChildViewController:self.tabbarVC]; //self.tabbarVC 表示的tabbar的controller
[self.view addSubview:self.tabbarVC.view];
self.currentVC = self.tabbarVC;? // self.currentVC 表示的是當前顯示的controller
然后? 在抽屜的第一欄的點擊事件- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// 點擊左側第一個 欄目? 1 表示 切換下面的第一個tabbar
[[NSUserDefaults standardUserDefaults] setObject:@"1" forKey:@"cutTabbar"];
[userDefaults synchronize];
調用 [sideViewController hideSideViewController:YES];?? 方法
}
在抽屜三方的 - (void)hideSideViewController:(BOOL)animated{?? “發通知 ”};
發消息通知?? 父視圖控制器接收到通知 就去判斷 父視圖當前顯示的視圖是不是左側的 controller 不是就切換成左側controller 。
那如何切回原來tabbar的 controller呢? 很簡單,同樣在我們自定義的XTabbarViewController類的 的點擊事件里 發消息通知? 父視圖控制器接收到通知 判斷當前顯示的controller 是不是tabbar 對應的controller ,不是就切換。父視圖 消息中心執行代碼:
-(void)myNotification:(NSNotification *)not
{
NSLog(@"childViewControllers === %@",self.childViewControllers);
NSDictionary *ddd =not.object;
NSLog(@"取出 幾 ? %@",not.object);
//0 代表 點擊的是tabbar 1代表點擊的是 左側的欄目
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); //顯示的是左側抽屜controller 切換成回顏色
if ([childType isEqualToString:self.tabbarType]) {
[self replaceController:self.tabbarVC newController:self.leftVC];
}else{
NSLog(@"不一樣");
}
// 切換了第一個tabbar的數據后 重置這個值 否則點擊回到左側抽屜時,即便不點擊左側第一個欄目也會切換成左側第一個欄目
[[NSUserDefaults standardUserDefaults] setObject:@"0" forKey:@"cutTabbar"];
}
}else{
self.tabBarController.tabBar.tintColor = UIColorFromRGBA(0xe13836);
}
}
//? 切換各個標簽內容
- (void)replaceController:(UIViewController *)oldController newController:(UIViewController *)newController
{
/**
*??????????? 著重介紹一下它
*? transitionFromViewController:toViewController:duration:options:animations:completion:
*? fromViewController????? 當前顯示在父視圖控制器中的子視圖控制器
*? toViewController??????? 將要顯示的姿勢圖控制器
*? duration??????????????? 動畫時間(這個屬性,old friend 了 O(∩_∩)O)
*? options???????????????? 動畫效果(漸變,從下往上等等,具體查看API)
*? animations????????????? 轉換過程中得動畫
*? completion????????????? 轉換完成
*/
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的圖片設置是 在本地放幾張圖片,名字要寫成“xxx@2.png”?? 用 [UIImage imageNamed:] 去設置,為什么要把名字寫成這樣呢?自己百度一下。然而我們的tabbar根據后臺設定了,那他的圖片不可能放到我們本地吧,所以要用后臺給的圖片網址去設置了。這個問題也是搞了好久,我用【UIImage imageWithData:】,然后處理了一下大小? 圖片是可以顯示上去,但是一直很模糊。最后想到用沙盒存儲,終于解決了這個問題 。存的 代碼:
NSString *str = [NSString stringWithFormat:@"tabbarIma%d@2x.png",i];// i表示的是第幾個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表示不壓縮,數值越小壓縮比例越大
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];
其實利用沙盒 ,無非就是為了存儲的時候可以給圖片 賦一個"xxx@2x"的名字
4.遇到的問題: 消息推送的跳轉
以前寫死的tabbar的app里? 假如你把消息推送的跳轉寫在 第一個tabbar的controller上 ,那你的當前界面如果在其他tabbar里收到了通知,你點擊查看,是不是跳轉不過去,除非你在點擊事件里改變tabbar的索引為0。 那我們的tabbar都寫活了,你都不知道哪個controller要被設置成第一個tabbar,所以這種方法無法解決 。找了一些資料 看了看,發現基類可以解決,而且都不需要去改變tabbar的索引,試了一下果然可以。我頓時對基類這個東西刮目相看了。
解決的方法:
在基類里 寫:
-(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];
}
}
發通知的地方 當然是appdelegate 的
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
userInfo 里面是推送過來的數據 你可以在這里寫一個 alertController? 在他的確定時間里 寫你的發送通知的代碼。
}
5. 遇到的問題 里面還會遇到一些小的問題,比如每一個controller的 布局問題, 如果這個controller被設置成tabbar了 那么它里面的 tableview或者collectionView 的fram 的高度是不是 要減去49 。這個需要在controller里做判斷的。等等一些小問題 就不一一列舉了。****