多控制器
- 當(dāng)app中有多個控制器的時候,我們就需要對這些控制器進(jìn)行管理枚荣,思路是用1個控制器去管理其他多個控制器罚攀,成為控制器的控制器吹榴,稱其為父控制器
- 為了便于管理控制器肌厨,iOS提供了2個比較特殊的控制器
- UINavigationController
- UITabBarController
UINavigationController
- 父類是UIViewController
導(dǎo)航控制器與子控制器
1. 導(dǎo)航條的內(nèi)容由棧頂控制器決定,一個導(dǎo)航控制器只有一個導(dǎo)航條,因此只能由一個控制器決定,誰先顯示在最外面,誰就是棧頂控制器.
2. 導(dǎo)航控制器永遠(yuǎn)顯示的是棧頂控制器的view
3. 導(dǎo)航控制器中做界面之間的跳轉(zhuǎn)必須拿到導(dǎo)航控制器,導(dǎo)航控制器的子控制器可以直接拿到導(dǎo)航控制器
4. 調(diào)用pop方法并不會馬上銷毀當(dāng)前控制器
5. popToViewController使用注意點(diǎn),傳入進(jìn)去的控制器必須是導(dǎo)航控制器棧里面的控制器
導(dǎo)航控制器的結(jié)構(gòu)
導(dǎo)航控制器內(nèi)部結(jié)構(gòu)(UINavigationBar(導(dǎo)航條),導(dǎo)航控制器的view,存放導(dǎo)航控制器子控制器的view的容器view)
導(dǎo)航條的內(nèi)容由棧頂控制器的navigationItem決定,因此導(dǎo)航控制器必須要有一個根控制器,因?yàn)樗旧聿痪邆渫暾娘@示功能,連自身的導(dǎo)航條也不能決定凰狞。
導(dǎo)航條上的子控件位置不由我們管理,只能管理尺寸
UINavigationItem:是一個模型,決定導(dǎo)航條的內(nèi)容(左邊內(nèi)容,中間,右邊內(nèi)容)
UIBarButtonItem:是一個UINavigationItem上按鈕的模型,決定導(dǎo)航條上按鈕的內(nèi)容
導(dǎo)航控制器-利用storyboard創(chuàng)建
1. 程序一啟動,就加載導(dǎo)航控制器,設(shè)置storyboard箭頭指向?qū)Ш娇刂破?2. 設(shè)置導(dǎo)航控制器的根控制器為UIViewController
3. 設(shè)置導(dǎo)航條的內(nèi)容,還有下一個控制器的返回按鈕
4. 利用storyboard做跳轉(zhuǎn),選中按鈕拖線
5. 利用按鈕,通過代碼實(shí)現(xiàn)回到上一個控制器,注意不能回拖,這樣不是指回到上一個控制器篇裁,而是跳轉(zhuǎn)到一個新的控制器。
6. 相比起storyboard創(chuàng)建UIViewController赡若,設(shè)置了窗口的根控制器為導(dǎo)航控制器后达布,還需要設(shè)置導(dǎo)航控制器的根控制器為UIViewController,并做好跳轉(zhuǎn)回返回設(shè)置
導(dǎo)航控制器的創(chuàng)建(代碼)
// 1.創(chuàng)建窗口
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 2.設(shè)置窗口的根控制器
// 創(chuàng)建導(dǎo)航控制器的根控制器
ViewController *vc = [[ViewController alloc] init];
// 導(dǎo)航控制器需要根控制器
// 導(dǎo)航控制器會把根控制器作為它的子控制器
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
// initWithRootViewController:底層其實(shí)就是調(diào)用push方法,把根控制器作為導(dǎo)航控制器的子控制器壓入棧中
// 導(dǎo)航控制器會把子控制器的view添加到導(dǎo)航控制器專門存放子控制器的view上
// [nav pushViewController:vc animated:YES];
self.window.rootViewController = nav;
// 3.顯示窗口
[self.window makeKeyAndVisible];
導(dǎo)航控制器的子控制器
- 子控制器類型
//獲取棧頂控制器 nav.topViewController //模型控制器或棧頂控制器 nav.visibleViewController //壓入棧的控制器(導(dǎo)航控制器的子控制器) @property(nonatomic,copy) NSArray *viewControllers; @property(nonatomic,readonly) NSArray *childViewControllers;//只讀 //棧底控制器逾冬,pop不會被銷毀,只是移除父視圖 nav.rootViewController
子控制器管理原理
導(dǎo)航控制器是通過棧管理子控制器,棧是先進(jìn)后出-
導(dǎo)航控制器的入棧操作
1. 子控制器用到時跳轉(zhuǎn)黍聂,不是一開始全部入棧:
1. 開發(fā)中是先給導(dǎo)航控制器添加根控制器,顯示根控制器的view,不需要顯示其他子控制器的view身腻。創(chuàng)建根控制器的方法(底層都是push方法壓入棧):
1. 直接根據(jù)根控制器創(chuàng)建導(dǎo)航控制器
2. 創(chuàng)建一個控制器产还,將控制器加入到導(dǎo)航控制器子控制器中,默認(rèn)第一個控制器是根控制器
3. 創(chuàng)建一個控制器然后push到導(dǎo)航控制器的棧中2. 跳轉(zhuǎn)控制器的權(quán)利應(yīng)該交給用戶,由用戶決定進(jìn)入那個界面 3. 用戶點(diǎn)擊根控制器的跳轉(zhuǎn)按鈕嘀趟,根控制器通過`push方法`將子控制器壓入棧脐区,并顯示控制器對應(yīng)的view
//push方法能將某個控制器壓入棧 - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
-
導(dǎo)航控制器的出棧操作
1. 自動出棧點(diǎn)擊back返回,移除棧頂控制器,移除的控制器會被銷毀
2. 主動出棧:通過pop手動出棧
1. 主動出棧,要求出棧的控制器必須是棧里面的控制器,不能自己創(chuàng)建一個控制器出棧,會報出棧的控制器不存在的錯誤,可以用viewControllers或者childViewControllers拿到根控制器。
2. pop控制器,不會馬上銷毀棧頂控制器,而是告訴導(dǎo)航控制器需要把棧頂控制器出棧,等到恰當(dāng)?shù)臅r間就會把棧頂控制器出棧,并且銷毀她按。//pop方法可以移除控制器 //將棧頂?shù)目刂破饕瞥?- (UIViewController *)popViewControllerAnimated:(BOOL)animated; //回到指定的子控制器 - (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated; //回到根控制器(棧底控制器) - (NSArray *)popToRootViewControllerAnimated:(BOOL)animated;
UINavigationController對UIScrollView的邊距調(diào)整
- 默認(rèn)情況下, 如果一個控制器處在導(dǎo)航控制器管理中, 并且控制器的view是UIScrollView, 那么就會自動調(diào)整這個UIScrollView的contentInset
1. UIEdgeInsetsMake(64, 0, 0, 0) // 導(dǎo)航條顯示
2. UIEdgeInsetsMake(20, 0, 0, 0) // 導(dǎo)航欄隱藏
- 在第1點(diǎn)的基礎(chǔ)上坡椒,若導(dǎo)航控制器又處在UITabBarController管理中, 那么就會自動調(diào)整這個UIScrollView的contentInset為
1. UIEdgeInsetsMake(64, 0, 49, 0)
- 如何禁止上述的影響?
viewController.automaticallyAdjustsScrollViewInsets = NO;
若該控制器的view不是UIScrollView或其子類,而是UIWebView尤溜,也會有這種自動調(diào)整效果倔叼,因?yàn)閁IWebView內(nèi)部有個scrollowView
注意:如下兩種情況是沒有邊距調(diào)整效果的
1. 導(dǎo)航控制器對子控制器的邊距調(diào)整,僅是對子控制器的View進(jìn)行調(diào)整宫莱;子控制器的View的子控件是UIScrollView及其子類則不受影響
2. 如果控制器只是處在標(biāo)簽控制器(UITabBarController)管理中丈攒,而沒有被導(dǎo)航控制器管理,也不會有這種效果上述影響的作用:實(shí)現(xiàn)穿透導(dǎo)航欄和標(biāo)簽條效果
1. IOS7之前授霸,tableView不是占據(jù)整個屏幕巡验,只是占據(jù)一部分,所以7之前沒有穿透效果碘耳。
2. IOS7之后显设,tableView可以占據(jù)整個屏幕,上述默認(rèn)效果可以使得tableView穿透導(dǎo)航欄和標(biāo)簽條效果辛辨。
控制器的數(shù)據(jù)傳遞:順傳和逆?zhèn)?/h3>
1. 順傳
1. 控制器的跳轉(zhuǎn)方向 : A -> C
2. 數(shù)據(jù)的傳遞方向 : A -> C
3. 數(shù)據(jù)的傳遞方式 :
1. 在A的prepareForSegue:sender:方法中根據(jù)segue參數(shù)取得destinationViewController(控制器C)
2. 直接給控制器C傳遞數(shù)據(jù)捕捂,然后在控制器C的viewDidLoad方法中取得數(shù)據(jù),來賦值給界面上的UI控件
2. 逆?zhèn)? 1. 控制器的跳轉(zhuǎn)方向 : A -> C
2. 數(shù)據(jù)的傳遞方向 : C <- A
3. 數(shù)據(jù)的傳遞方式 : 讓A成為C的代理, 在C中調(diào)用A的代理方法,通過代理方法的參數(shù)傳遞數(shù)據(jù)給A瑟枫;或者通過通知,也應(yīng)該可以用block回傳
1. 順傳
1. 控制器的跳轉(zhuǎn)方向 : A -> C
2. 數(shù)據(jù)的傳遞方向 : A -> C
3. 數(shù)據(jù)的傳遞方式 :
1. 在A的prepareForSegue:sender:方法中根據(jù)segue參數(shù)取得destinationViewController(控制器C)
2. 直接給控制器C傳遞數(shù)據(jù)捕捂,然后在控制器C的viewDidLoad方法中取得數(shù)據(jù),來賦值給界面上的UI控件
2. 逆?zhèn)? 1. 控制器的跳轉(zhuǎn)方向 : A -> C
2. 數(shù)據(jù)的傳遞方向 : C <- A
3. 數(shù)據(jù)的傳遞方式 : 讓A成為C的代理, 在C中調(diào)用A的代理方法,通過代理方法的參數(shù)傳遞數(shù)據(jù)給A瑟枫;或者通過通知,也應(yīng)該可以用block回傳