1. 起因
我們經(jīng)常能夠在第三方庫的源碼中看到很多loadView
、willMoveToParentViewController:
蜡秽、viewDidLayoutSubviews
等等諸如此類的并不是十分常見的方法车荔。這讓永遠都只在viewDidLoad
寫作的童鞋們情何以堪吶锻弓。
這些其實都和生命周期有關,和viewController以及view的各種加載順序有關蔫骂。這篇文章就小小擼一下這中間的關系和順序么翰。
2. Controller的生命周期
- 系統(tǒng)提供了控制器從顯示到消失的四個方法。
- 千萬不要看到方法名中間出現(xiàn)了
view
就以為這是視圖的方法纠吴。這些都是控制器的生命周期硬鞍。
四個方法如下:
//將要顯示,一定要調用super
- (void)viewWillAppear:(BOOL)animated; // Called when the view is about to made visible. Default does nothing
//視圖已經(jīng)顯示
- (void)viewDidAppear:(BOOL)animated; // Called when the view has been fully transitioned onto the screen. Default does nothing
//視圖將要消失
- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing
//視圖已經(jīng)消失
- (void)viewDidDisappear:(BOOL)animated; // Called after the view was dismissed, covered or otherwise hidden. Default does nothing
那四個階段都有什么區(qū)別呢戴已?啥時候用什么呢固该?
2.1 viewWillAppear
當view即將被顯示時調用,此時superview 為nil,也就是說這個是controller還不知道superview是誰糖儡。
這個階段會加載一些高開銷的操作伐坏,例如:鍵盤彈出、特殊的過程動畫(比方說修改狀態(tài)欄握联、導航條顏色等等)這些桦沉。
2.2 viewDidAppear
這個方法表面上看上和viewDidLoad沒有什么區(qū)別啊每瞒。
但是請注意一下細節(jié)。官方是這么描述viewDidLoad
纯露。
Called after the view has been loaded剿骨,F(xiàn)or view controllers unarchived from a nib, this is after the view is set.
什么意思?意思是說viewDidLoad
在視圖被加載后調用埠褪。如果使用了布局文件浓利,那么會在布局文件加載后被調用。
我們再來看看viewDidAppear
的描述:
Called when the view has been fully transitioned onto the screen
意思是視圖出現(xiàn)在屏幕上之后才調用钞速。
為了能夠進一步搞清楚之間的區(qū)別贷掖,我們在不同屏幕上運營一下這兩個方法看看調度的時間點。我們將Xcode默認使用5S去設置一下渴语,屏幕大小是320*568苹威,但是如果運行在6s上會怎么樣。下面做一個小測試驾凶,打印在不同方法執(zhí)行的過程中牙甫,界面的長寬屬性如何,結果如下:
//viewDidLoad w:320.000000 h:568.000000
//viewWillAppear w:320.000000 h:568.000000
//viewDidAppear w:414.000000 h:672.000000
有沒有發(fā)現(xiàn)在viewWillAppear
狭郑、viewDidAppear
獲取的長寬不一致腹暖??所以系統(tǒng)在這兩者之間肯定存在一個屏幕適配的過程翰萨。
- 從上面的結果可以看到,如果需要調整空間的frame糕殉,其實是放在
viewDidAppear
中最靠譜的亩鬼。 - 在自定義cell的時候也有這種問題,如果在init中添加控件的話阿蝶,那么self.frame.size.width也不一定是準確的寬度雳锋,一般解決方法是使用[UIScreen mainScreen].bounds.size.width而不是self.frame.size.width。
2.3 viewWillDisappear
Called when the view is dismissed, covered or otherwise hidden. Default does nothing
視圖被駁回時調用羡洁,覆蓋或以其他方式隱藏玷过。默認情況下不執(zhí)行任何操作。
貌似看上去這個方法沒啥用處筑煮,但是隨著iPhone手機屏幕越來越大辛蚊,左上角的返回按鈕早已夠不到(很多妹子的手都沒有那么大哦!)真仲,所以小手的吃瓜群眾通常喜歡通過側滑的形式返回上一個界面袋马。
而這種側滑返回會出現(xiàn)什么問題呢?當觸發(fā)側滑返回時會調用系統(tǒng)自帶的viewWillDisappear:方法秸应。
- iOS7新增加了導航控制器側滑手勢虑凛,當觸發(fā)側滑返回時碑宴,會調用系統(tǒng)的
viewWillDisappear:
方法,取消側滑返回時又會調用viewWillAppear:
方法桑谍。
2.4 viewDidDisappear
Called after the view was dismissed, covered or otherwise hidden. Default does nothing
對象的視圖已經(jīng)消失延柠、被覆蓋或是隱藏時調用.
UIViewController
類提供一些方法,用來判斷為什么view外觀發(fā)生更改锣披。
- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);
- (BOOL)isBeingDismissed NS_AVAILABLE_IOS(5_0);
- (BOOL)isMovingToParentViewController NS_AVAILABLE_IOS(5_0);
- (BOOL)isMovingFromParentViewController NS_AVAILABLE_IOS(5_0);
在四個方法中:
isMovingFromParentViewController 會在viewWillDisappear
& viewDidDisappear
方法內部調用這個方法判斷視圖控制器的視圖的隱藏是否因為視圖控制器從它的容器視圖控制器移除贞间。
isBeingDismissed 會在viewWillDisappear
& viewDidDisappear
方法內部調用這個方法判斷視圖控制器的視圖的隱藏是否因為視圖控制器被清退 (dismissed,與上面被其它視圖控制器顯示對應盈罐,如信息錄入完成榜跌,返回之前的視圖控制器)。
2.5 控制器View的生命周期
-
loadView
:加載view- 作用:用來創(chuàng)建控制器的View盅粪。在執(zhí)行的時候會首先判斷有沒有指定的storyboard或者Xib钓葫,如果指定,就會加載它們描述的控制器的View票顾,如果沒有指定础浮,創(chuàng)建一個空的View。
- 調用時刻:每次訪問Controller的View奠骄,當View為nil豆同,就會調用loadView方法。
-
ViewDidLoad
:view加載完畢- 當控制器的loadView方法執(zhí)行完畢含鳞,view被創(chuàng)建成功后影锈,就會執(zhí)行viewDidLoad方法。
ViewWillAppear
:view將要顯示ViewWillLayoutSubViews
:view將要布局子控件ViewDidLayoutSubViews
:view布局子控件完成ViewDidAppear
:view完全顯示ViewWillDisAppear
:view即將消失ViewDidDisAppear
:view完全消失
3. View的生命周期
我們知道view的創(chuàng)建有init
(或new
或者跟類名一樣的)的方法蝉绷,銷毀時會自動執(zhí)行dealloc
方法鸭廷,但是UIView的生命周期到底是怎樣的呢?系統(tǒng)也同樣提供了四個方法用來管理四個不同時期的內容熔吗,有一點需要注意的是這四個方法都會執(zhí)行辆床,只是添加或者移除的父視圖不同。
//將要添加到父視圖上桅狠,要執(zhí)行addSubview
- (void)willMoveToSuperview:(nullable UIView *)newSuperview;
//已經(jīng)添加到父視圖上
- (void)didMoveToSuperview;
//將要添加到窗口
- (void)willMoveToWindow:(nullable UIWindow *)newWindow;
//已經(jīng)添加到窗口
- (void)didMoveToWindow;
- 某個視圖的層次一改變讼载,該視圖就會收到一次回調。
- 調用addSubivew:成功后會給該視圖發(fā)送didAddSubivew:回調中跌,觸發(fā)UIView的子類在新增視圖時執(zhí)行其他操作咨堤。
- didMoveToSuperview:會通知相關視圖他們的上級視圖已經(jīng)變化。添加和移除都會調用,所以要判斷 superView在不在晒他。
- 視圖移動前會發(fā)出willMoveToSuperview:回調
- didMoveToWindow:回調和didMoveToSuperview:相似吱型,從命名上能看出其區(qū)別。
- willMoveToWindow:在視圖移動前發(fā)出的回調陨仅。
- willRemoveToSubview:回調通知父視圖子視圖即將被刪除
4. 內存警告
- 首先要判斷一下津滞,當前view有沒有被顯示铝侵。如果正在顯示,做處理触徐,會讓用戶感覺很不舒服咪鲜。
- 更嚴謹一點,還需要判斷view是否已經(jīng)加載撞鹉。如果沒有加載疟丙,就不需要干掉了。
- 實際開發(fā)中為了寫的少點鸟雏,都會寫在基類控制器中享郊。
- 官方說,iOS 6.0以后系統(tǒng)就不會自動清理孝鹊,需要手動清理炊琉。
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// 第一個判斷條件:當這個view是否正在顯示
// 第二個判斷條件:這個view是否已經(jīng)被加載
if (self.isViewLoaded && self.view.window == nil) {
[self setView:nil];
}
·