- 父類是UIResponder(父類是NSObject)
- 概念:凡是繼承自UIViewController的對象,都叫做控制器
- 作用:負(fù)責(zé)處理軟件界面的各種事件、負(fù)責(zé)軟件界面的創(chuàng)建和銷毀
- 注意:每個UIViewController專門管理一個軟件界面(view),它管理的其他所有控件都是這個view的子控件或“孫控件”
// UIViewController用view這個強指針指著它負(fù)責(zé)的軟件界面,所以只要控制器不銷毀,軟件界面就不會銷毀 NSLog(@"%@",viewController.view);
控制器的創(chuàng)建
通過storyboard創(chuàng)建控制器
- 詳見storyboard中“系統(tǒng)加載指定storyboard的流程”
通過xib創(chuàng)建控制器
- 步驟:
1. 首先創(chuàng)建一個Xib文件
2. Xib文件需要拖一個View描述控制器的View
3. 需要把Xib上的View與控制器連線,并設(shè)置Xib的File'owner為控制器// 通過xib創(chuàng)建窗口的根控制器:initWithNibName // NibName:xib名稱 ViewController *vc = [[ViewController alloc] initWithNibName:@"VC" bundle:nil]; self.window.rootViewController = vc;
- xib注意點:
1> xib里面必須有一個view描述控制器的view,因為控制器的view 屬性必須有值。
2> xib需要指定描述哪一個控制器,描述UIView不需要,因為xib里面可以描述很多UIView,不能固定死,但是控制器就不一樣了,一個xib就用來描述一個控制器。(設(shè)置file owner為控制器,即設(shè)置custom class)
3> xib里面可能有很多view,需要拖線指明哪個是控制器的view
- 假設(shè)通過Xib創(chuàng)建XXXViewController控制器對象順序
1. 如果nibName不為空,會去創(chuàng)建對應(yīng)的xib
2. 如果nibName為nil,首先會去查找XXXViewController.xib,如果有,就會去加載XXXViewController.xib
3. 如果沒有找到,就會去找XXXView.xib,如果有,也會去加載
4. 都沒有找到,就會生成一個空的view
5. init底層就會調(diào)用initWithNibName:bundle:(注意不會通過storyboard的方式加載)
6. Xcode7之前是先找XXXView后找XXXViewController
xib如何快速創(chuàng)建控制器的view:
1> 定義新的控制器的時候,勾選xib,會自動搞一個xib描述控制器的view.
2> 會自動生成一個和控制器同名的xib,并且里面設(shè)置好了。xib最后會轉(zhuǎn)成Nib文件,關(guān)于nib的介紹詳見UINib
xib和storyboard的區(qū)別 storyboard已經(jīng)指定了控制器的view,不需要我們管,xib需要我們手動設(shè)置view的custom class和owner。
控制器的view
-
控制器View的決定權(quán):[viewController setView:view]——>viwDidLoad——>重寫LoadView>storyboard>xib
控制器view加載.png
- 控制器的view是懶加載的
-(UIView *)view { if (_view == nil) { [self loadView]; [self viewDidLoad]; } return _view; }
- loadView的時機與重寫loadView的意義
1. 第一次加載控制器的view時才會調(diào)用loadView
2. 使用場景:若想自定義控制器的View就可重寫這個方法,例如:控制器的View想展示一張圖片,或者創(chuàng)建UIWebView等;
3. 注意:一旦重寫了loadView,就不要調(diào)用[super loadView],否則會重復(fù)創(chuàng)建- (void)loadView { // 如果當(dāng)前控制器是窗口的根控制器,它的view可以不設(shè)置尺寸。 UIImageView *vcView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"1"]]; self.view = vcView; }
可以用isViewLoaded方法判斷一個UIViewController的view是否已經(jīng)被加載
只要創(chuàng)建了控制器,無論是通過storyboard,還是xib,還是手動,都會自動創(chuàng)建view,如果沒有指定view,創(chuàng)建的是一個空的view
- 控制器view的透明度
//alloc init創(chuàng)建空View,默認(rèn)幾乎透明(幾乎透明:alpha > 0.01) ViewController *vc = [[ViewController alloc] init]; vc.view.backgroundColor = [UIColor clearColor];//空view默認(rèn)的顏色 //vc.view.backgroundColor = [UIColor whiteColor]; // 如果一個View完全透明,可以直接點擊后面的東西,可以完全穿透。(完全透明:alpha <= 0.01);另外要注意顏色與透明度無關(guān) vc.view.alpha = 0.02;
控制器的生命周期方法
-
生命周期方法:即控制器的view什么時候創(chuàng)建,什么時候銷毀,只要以view開頭一般都是控制器view的生命周期
生命周期方法.png
- 具體的生命周期方法:(布局控件方法調(diào)用頻繁,view顯示與消失前都會調(diào)用)
// 控制器的view加載完成時調(diào)用
- (void)viewDidLoad {
[super viewDidLoad];
//以后打印控制器的View真實尺寸,一般不再viewDidLoad去打印,因為不準(zhǔn)確(還在加載數(shù)據(jù)),在viewDidAppear中打印
}
// 控制器的view即將顯示時調(diào)用
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
// 控制器的view完全顯示時調(diào)用
// 一般用來調(diào)試控制器view的真實尺寸
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
// 控制器的view即將消失時調(diào)用
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
// 控制器的view完全消失的時候調(diào)用
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
// 控制器的view即將布局子控件的時候調(diào)用
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
}
// 控制器的view布局子控件的完成時候調(diào)用
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
}
// ARC中必須要了解
// viewDidLoad -> viewWillAppear -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidAppear -> viewWillDisappear -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidDisappear //可以看出布局控件方法調(diào)用頻繁
// 非ARC
// Unload:卸載,當(dāng)前view銷毀
//- (void)viewWillUnload
//{
//}
// 當(dāng)前view卸載完成,完全銷毀的時候調(diào)用
- (void)viewDidUnload
{
self.datas = nil;
}
// MRC中內(nèi)存機制解釋
//- (void)setDatas:(NSArray *)datas
//{
// if (datas != _datas) {
// [_datas release];
// _datas = [datas retain];
// }
//}
當(dāng)出現(xiàn)內(nèi)存警告時,控制器的處理流程
- didReceiveMemoryWarning,當(dāng)控制器接收內(nèi)存警告的時候調(diào)用
-
內(nèi)存警告?zhèn)鬟f過程:手機內(nèi)存不足產(chǎn)生事件->通知應(yīng)用程序->調(diào)用應(yīng)用程序代理方法->把事件傳遞給窗口->窗口傳給控制器->調(diào)用控制器內(nèi)存警告的方法。
內(nèi)存警告處理.png
-
當(dāng)控制器接收內(nèi)容警告,會銷毀沒有顯示的控制器的view。
調(diào)用viewWillUnload,viewDidUnload,銷毀控制器的view
viewDidUnload里面一般清空顯示在view里面的數(shù)據(jù)
1. 為什么要清空顯示view的數(shù)據(jù):展示數(shù)據(jù)的view都不存在了,這些數(shù)據(jù)也就沒有用處了,因為數(shù)據(jù)主要是用來展示在view上
2. 清空數(shù)據(jù)建議使用nil,清空數(shù)據(jù),在非arc和arc都通用。arc是不能使用release,而且非arc下,self.datas = nil;做的事情更多。-
didReceiveMemoryWarning會導(dǎo)致viewDidLoad重新調(diào)用。
- 當(dāng)收到內(nèi)存警告,導(dǎo)航控制器的子控制器的view有可能被干掉,他如果沒有顯示的話,當(dāng)下次使用這個控制器的時候就會調(diào)用,加載view。
UIViewController管理狀態(tài)欄
從iOS7開始,系統(tǒng)提供了2種管理狀態(tài)欄的方式
-
在iOS7中,默認(rèn)情況下,狀態(tài)欄都是由UIViewController管理的(每一個UIViewController都可以擁有自己不同的狀態(tài)欄),UIViewController實現(xiàn)下列方法就可以輕松管理狀態(tài)欄的可見性和樣式
// 設(shè)置狀態(tài)欄是否隱藏 viewController.prefersStatusBarHidden = YES; // 修狀態(tài)欄樣式 - (UIStatusBarStyle)preferredStatusBarStyle { return UIStatusBarStyleLightContent; } @end
-
通過UIApplication管理(一個應(yīng)用程序的狀態(tài)欄都由它統(tǒng)一管理)
1. 需在info.plist中配置
Snip20151108_152.png2. 在Info.plist中做了圖中的配置,可能會出現(xiàn)以下警告信息
```objc
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
// 狀態(tài)欄的可見性
// 隱藏狀態(tài)欄
app.statusBarHidden = YES;
// 動畫隱藏狀態(tài)欄
[app setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide]
```
父子控制器
- 開發(fā)原則:如果子控件添加到父控件中時,如果子控件是控制器的view,需要將子控件的控制器添加到父控件的控制器。
- 屏幕控件切換顯示時,某個控件不顯示,一般是將控件從父控件移除,但不會立即移除對應(yīng)的子控制器。
一來方便下次使用時快速處理時間與顯示視圖;
二來是子控制器銷毀時,其視圖也會被銷毀,而不會造成內(nèi)存泄露。 - 開發(fā)中遇到內(nèi)存警告時,沒有顯示的控件,要銷毀其控制器以釋放內(nèi)存。
- 任何控制器都可以添加子控制器,都可以管理子控制器
- 父控制器的UINavigationController(導(dǎo)航控制器)和UITabBarController(標(biāo)簽控制器),它的子控制器都能拿到,它的底層是:
1. 判斷下自己是不是導(dǎo)航控制器和標(biāo)簽控制器的子控制器,ChildViewController不是導(dǎo)航控制器和標(biāo)簽控制器子控制器則繼續(xù)判斷
2. 判斷下父控制器是不是導(dǎo)航控制器和標(biāo)簽控制器的子控制器,是就可以拿到
3. 繼續(xù)一直判斷,直到?jīng)]有父控制器 -
Modal也是同樣的原理:
1. 判斷下自己是否被Modal,如果被modal,就把自己dismiss
2. 如果不是,判斷下自己父控制器是否被modal,如果被modal,就把自己dismiss
3. 一直判斷,直到?jīng)]有父控制器
// 子控制器(只讀)
@property(nonatomic,readonly) NSArray<__kindof UIViewController *> *childViewControllers NS_AVAILABLE_IOS(5_0);
// 添加子控制器
- (void)addChildViewController:(UIViewController *)childController NS_AVAILABLE_IOS(5_0);
// 移除出父控制器
- (void) removeFromParentViewController
UIViewController常用屬性
// 標(biāo)題
@property(nullable, nonatomic,copy) NSString *title;
// 父控制器
@property(nullable,nonatomic,weak,readonly) UIViewController *parentViewController;
// modal它的控制器
@property(nullable, nonatomic,readonly) UIViewController *presentedViewController NS_AVAILABLE_IOS(5_0);
// 它modal的控制器
@property(nullable, nonatomic,readonly) UIViewController *presentingViewController NS_AVAILABLE_IOS(5_0);
判斷當(dāng)前ViewController是push還是present的方式顯示的
NSArray *viewcontrollers=self.navigationController.viewControllers;
if (viewcontrollers.count > 1)
{
if ([viewcontrollers objectAtIndex:viewcontrollers.count - 1] == self)
{
//push方式
[self.navigationController popViewControllerAnimated:YES];
}
}
else
{
//present方式
[self dismissViewControllerAnimated:YES completion:nil];
}