實(shí)際開發(fā)中都是多控制器的;用一個(gè)控制器(父)管理多個(gè)控制器(子)
ios提供2個(gè)特殊的(父)控制器
UINavigationControler
- 簡(jiǎn)介
導(dǎo)航控制器,可以輕松完成多個(gè)控制器之間的切換,其結(jié)構(gòu)包含導(dǎo)航條(y=20)、棧頂控制器的view、導(dǎo)航控制器的view。導(dǎo)航控制器需要設(shè)置一個(gè)根控制器,一般是UIViewControler。 - 基本使用

最開始的時(shí)候,棧頂控制器的view就是導(dǎo)航控制器的根控制器的view。
1.先去掉Info.plist的倒數(shù)第三行的main,以采用代碼方式創(chuàng)建

2.在AppDelegate.m中:

其中,先創(chuàng)建窗口,再創(chuàng)建導(dǎo)航控制器(同時(shí)創(chuàng)建了一個(gè)UIviewControler的控制器,作為其根控制器),然后把該窗口的根控制器設(shè)為所創(chuàng)建的導(dǎo)航控制器,最后顯示窗口。
PS:一般地,導(dǎo)航控制器的根控制器來自新建一個(gè)UIViewControler類,然后import進(jìn)來。另外,只要一個(gè)控制器是導(dǎo)航控制器的子控制器,那么導(dǎo)航控制器就會(huì)成為這個(gè)控制器的一個(gè)屬性(可以拿到 )
- 控制器之間的跳轉(zhuǎn):

根控制器的導(dǎo)航控制器調(diào)用pushViewControler方法,參數(shù)為跳轉(zhuǎn)后的控制器。跳轉(zhuǎn)后,顯示的是后來這個(gè)ViewControler的view,也成了棧頂控制器的view,同時(shí)從window上移除了前一個(gè)控制器的view,但仍保存于導(dǎo)航控制器的子控制器數(shù)組中。
PS:導(dǎo)航控制器是其根控制器的一個(gè)屬性。
- 導(dǎo)航控制器的子控制器
一般保存在數(shù)組(以棧的形式)(先進(jìn)后出)中

PS:使用setViewControllers方法可以一次壓入多個(gè)控制器,但是只會(huì)顯示最后壓入的那個(gè)控制器的view(棧頂控制器)
[nav setViewControllers:@[vc1,vc2,vc3] animated:YES];
獲取棧頂控制器:navController.topViewController
獲取當(dāng)前可見視圖的控制器:.visibleViewController
-
控制器的返回
是跳轉(zhuǎn)操作的逆向。執(zhí)行返回操作后,先從window中移除當(dāng)前控制器的view,同時(shí)把該控制器(在棧頂)從棧中移除。此時(shí)在棧頂?shù)目刂破鞯膙iew會(huì)添加到window,顯示出來,從而實(shí)現(xiàn)返回。
QQ.png
注意:
要回到指定的控制器,這個(gè)指定的控制器必須得是導(dǎo)航控制器的子控制器。
當(dāng)一個(gè)控制器被pop后,控制器內(nèi)存就被釋放了(會(huì)調(diào)用deinit/dealloc函數(shù)。
控制器被銷毀,它的view不一定就銷毀(只要有storng指針),但是當(dāng)一個(gè)控制器被銷毀,那么它的view的業(yè)務(wù)邏輯無法處理。
設(shè)置導(dǎo)航條的內(nèi)容
由棧頂控制器的navigationItem屬性來決定導(dǎo)航條的內(nèi)容,在棧頂控制器的.m的viewDidLoad{ }中設(shè)置

其中,titleView可以設(shè)為各種控件


設(shè)置navBar的字體大小,顏色
[nav.navigationBar setTitleTextAttributes:@{
NSFontAttributeName:[UIFont systemFontOfSize:19],
NSForegroundColorAttributeName:[UIColor whiteColor]
}];
navBar的隱藏狀態(tài):navController.navigationBarHidden = YES;
UINavigationBar通過UINavigationItem堆棧,按照如下方式來決定展示在UINavigationBar中的內(nèi)容
q
- 位于中間的標(biāo)題會(huì)根據(jù)下方順序選擇展示的內(nèi)容:
如果topItem設(shè)置了標(biāo)題視圖(titleView屬性), 則展示標(biāo)題視圖
如果topItem設(shè)置了標(biāo)題文字(title屬性), 則展示標(biāo)題文字
如果以上都未設(shè)置, 則展示空白。 - 位于右側(cè)的按鈕會(huì)根據(jù)下方順序選擇展示的內(nèi)容:
如topItem設(shè)置了右側(cè)按鈕(rightBarButtonItem屬性)則展示右側(cè)按鈕
如果以上都未設(shè)置, 則展示空白。 - 位于左側(cè)的按鈕會(huì)根據(jù)下方順序選擇展示的內(nèi)容:
如topItem設(shè)置了左側(cè)按鈕(leftBarButtonItem屬性), 則展示左側(cè)按鈕
如backItem設(shè)置了返回按鈕(backBarButtonItem), 則展示返回按鈕
如backItem設(shè)置了標(biāo)題(title), 則展示用標(biāo)題文字封裝的返回按鈕 - 如果以上都未設(shè)置, 則展示利用文字"Back"封裝的返回按鈕。
設(shè)置導(dǎo)航條的外觀
UINavigationBar類中提供了大量屬性/方法用于設(shè)置其外觀, 我們可以設(shè)置其樣式、背景顏色、色彩顏色、文字屬性,同樣, 我們也可以設(shè)置其背景圖片、陰影圖片。
- 樣式:navigationBar.barStyle
該屬性可為UIBarStyleDefault(白底黑字)、UIBarStyleBlack(黑底白字) - 背景色:navigationBar.barTintColor
- 前景色:navigationBar.tintColor
- 設(shè)置背景圖片:[navigationBar setBackgroundImage:。。。];
- 陰影圖片:navigationBar.shadowImage = [UIImage imageNamed:@""];
區(qū)分關(guān)系:
NavigaitonBar是導(dǎo)航欄,位于屏幕上方,管理整個(gè)導(dǎo)航控制器的navigationItem,它類似navigationcontroller一樣,提供了一個(gè)棧來管理UINavigationItem。在編程時(shí),一般只設(shè)置每個(gè)控制器的navigationItem屬性。
一個(gè)導(dǎo)航控制器管理多個(gè)視圖控制器(多個(gè)視圖控制器共享一個(gè)導(dǎo)航控制器),而一個(gè)導(dǎo)航控制器只有一個(gè)UINavigationBar,被管理的多個(gè)視圖控制器共享這一個(gè)UINavigationBar,只要一個(gè)視圖控制器改變了UINavigationBar的屬性則影響是全局的。
每個(gè)視圖控制器都會(huì)有屬于自己的UINavigationItem,系統(tǒng)會(huì)以懶加載的方式創(chuàng)建一個(gè)UINavigationItem顯示在UINavigationBar中,改變UINavigationItem只會(huì)在當(dāng)前控制器起作用,不會(huì)影響其它控制器。
通過storybord跳轉(zhuǎn)控制器
設(shè)根控制器為導(dǎo)航控制器(刪掉默認(rèn)的控制器,拖入nav,設(shè)之為默認(rèn)即添加箭頭,添加導(dǎo)航控制器的根控制器(viewcontroler),方法是采用拖線的方式,點(diǎn)擊rootcontroler;再拖入一個(gè)viewcontroler,實(shí)現(xiàn)控制器之間的跳轉(zhuǎn),方法是拖線(從一個(gè)按鈕拖到下一個(gè)控制器,點(diǎn)擊show選項(xiàng))
設(shè)置導(dǎo)航條的內(nèi)容(可以把其他控件如item等拖到導(dǎo)航條;可以設(shè)導(dǎo)航條標(biāo)題;
自定義導(dǎo)航控制器
- 設(shè)置導(dǎo)航條統(tǒng)一的樣式
+(void)initialize{
//獲取APP的導(dǎo)航條標(biāo)識(shí)
// UINavigationBar *bar = [UINavigationBar appearance]; //這種寫法有bug,它會(huì)代表app中所有的導(dǎo)航條,當(dāng)app中還有自定義導(dǎo)航條時(shí)也會(huì)被包含
UINavigationBar *bar = [UINavigationBar appearanceWhenContainedIn:[PDNavigationViewController class],nil]; //(獲取指定某幾個(gè)類的導(dǎo)航條標(biāo)識(shí))
//設(shè)置nav bar的背景圖片(因無法設(shè)置背景顏色)
[bar setBackgroundImage:[UIImage imageNamed:@"navBar"] forBarMetrics:UIBarMetricsDefault];
//標(biāo)題字體大小、顏色
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSFontAttributeName] = [UIFont systemFontOfSize:22.0];
dict[NSForegroundColorAttributeName] = [UIColor whiteColor];
[bar setTitleTextAttributes:dict];
}
統(tǒng)一樣式只需設(shè)一次即可。因此,在initialize方法中設(shè)置,此方法當(dāng)當(dāng)前類第一次初始化的時(shí)候才調(diào)用,這樣就實(shí)現(xiàn)只設(shè)置一次。當(dāng)然在加載時(shí)viewDidLoad方法也只調(diào)用一次,但是一個(gè)缺點(diǎn)就是,當(dāng)要?jiǎng)?chuàng)建多個(gè)導(dǎo)航控制器時(shí),會(huì)調(diào)用(設(shè)置)多次。
- 設(shè)置統(tǒng)一的返回按鈕
重寫push方法來實(shí)現(xiàn)
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
[super pushViewController:viewController animated:animated];
//判斷當(dāng)前不是在根控制器下,此時(shí)設(shè)置leftBarButtonItem
if(self.viewControllers.count > 1)
viewController.navigationItem.leftBarButtonItem =
[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"nearby"]
style:0 target:self action:@selector(back)];
}
-(void)back{
[self popViewControllerAnimated:YES]; //pop返回上層
}
(此時(shí)就系統(tǒng)沒有了滑動(dòng)返回功能)
- 恢復(fù)滑動(dòng)返回功能
原理:通過清空代理的方式可以恢復(fù)
self.interactivePopGestureRecognizer.delegate = nil;
但是存在bug,因?yàn)榍蹇罩髸?huì)帶來后續(xù)影響,改善如下:
@interface PDNavigationViewController ()<UINavigationControllerDelegate>
@property(nonatomic,strong) id popGesture; //用于暫存
@end
-(void)viewDidLoad {
[super viewDidLoad];
self.popGesture = self.interactivePopGestureRecognizer.delegate; //暫存代理
self.delegate = self; //將代理設(shè)為自身
}
在導(dǎo)航控制器代理方法中清空代理,此方法當(dāng)控制器顯示完畢時(shí)調(diào)用,因此能夠拿到當(dāng)前的控制器
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
//根控制器中不需滑動(dòng)返回,此時(shí)還原代理
if(self.viewControllers[0] == viewController){ //當(dāng)前是根控制器時(shí)
self.interactivePopGestureRecognizer.delegate = self.popGesture; //還原代理
}else{ //非根控制器時(shí),清空代理( 恢復(fù)滑動(dòng)返回功能 )
self.interactivePopGestureRecognizer.delegate = nil;
}
}
- 實(shí)現(xiàn)全屏滑動(dòng)返回( 系統(tǒng)默認(rèn)從左側(cè)滑動(dòng)才能返回 )
-(void)viewDidLoad {
[super viewDidLoad];
//以下代碼實(shí)現(xiàn)全屏滑動(dòng)返回
id target = self.interactivePopGestureRecognizer.delegate;
self.interactivePopGestureRecognizer.enabled = NO;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
[self.view addGestureRecognizer:pan];
pan.delegate = self;
}
解決全屏滑動(dòng)的bug
//當(dāng)開始滑動(dòng)就會(huì)調(diào)用
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
//如果是在非根控制器,才允許滑動(dòng)
BOOL open = self.viewControllers.count > 1;
return open;
}
