iOS應用界面切換
- 1、UIViewController 的生命周期;
- 2、push & pop;
- 3、presentModalView. (它是和 navigationController 無關的,是在所在的 viewController 添加一個模態 view)
iOS三種則視圖切換的原理各不相同 (from:Kenshin Cui's Blog)
- UITabBarController: 以平行的方式管理視圖,各個視圖之間往往關系不大,每個加入到UITabBarController的視圖都會進行初始化,即使當前不顯示在界面上
這兩句現在還不是很懂
,相對比較占用內存優化的一個入口嗎?
。[tabBarItem 的 image 屬性必須是png格式(建議32*32)并且打開alpha通道,否則無法正常顯示] - UINavigationController: 以棧的方式管理視圖,各個視圖的切換就是壓棧和出棧操作,出棧后的視圖會立即銷毀
釋放比較合適
。[只有在棧頂的控制器能夠顯示在界面中。UINavigationController默認也不會顯示任何視圖,它必須有一個根控制器rootViewController,而且這個根控制器不會像其他子控制器一樣會被銷毀。] - UIModalController: 以模態窗口的形式管理視圖,當前視圖關閉前,無法在其它的視圖上進行操作。
對其blog進行研讀并在以后做好筆記工作
(接下來先了解2和3的內容
)
下面的代碼了解push & pop 和 presentModalView 的方法。
在實現文件 .m 的 viewDidLoad 方法中輸入以下代碼:
"1號“代碼段
UIBotton *pushButton = [UIButton buttonWithType: UIButtonTypeCustom];
pushButton.frame = CGRectMake(10, 74, self.view.bounds.width - 20, 44);
[pushButton setBackgroundColor: [UIColor cyanColor]];
[pushButton setTittle: @"push a view" forState: UIControlStateNormal];
[pushButton setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
[pushButton addTarget: self
action: @selector(pushButtonClicked)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: pushButton];
接下來創建一個 presentButton,目的是為了演示 兩種不同的頁面切換方式:
“2號”代碼段
UIBotton *presentButton = [UIButton buttonWithType: UIButtonTypeCustom];
presentButton.frame = CGRectMake(10, 130, self.view.bounds.width - 20, 44);
[presentButton setBackgroundColor: [UIColor yellowColor]];
[presentButton setTittle: @"present a modal view" forState: UIControlStateNormal];
[presentButton setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
[presentButton addTarget: self
action: @selector(presentButtonClicked)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: presentButton];
上面有兩段 選擇器 selector 的代碼。通過它們使界面進行跳轉,不過首先要選定跳轉到哪一個頁面,在選定之后,在實現文件 .m 開頭導入跳轉后界面的控制器才會在執行方法后可以顯示界面:
“3號”代碼段
#import "BLSubViewController.h"
接下來,先介紹下 push 操作:它類似于 UINavigationController 的壓棧(先進后出)。下面演示上面兩個 Button 所關聯的 selector 跳轉方法,第一個介紹的是 push (注意它們要跳轉到的那個頁面已經選定,就是剛導入的 BLSubViewController):
“4號”代碼段
#pragma mark - Custom event methods
- (void)pushButtonClicked:(id)sender
{
BLSubViewController *subViewController = [[BLSubViewController] init];
[self.navigationController pushViewController:subViewController animated:YES];
}
也許在 animated: 中使用 YES 顯得更貼近自然語言吧。
??上面方法中的兩行代碼就已經通過 push 將頁面進行了跳轉。 [首先創建了一個跳轉目的界面的視圖控制器,然后由當前界面視圖控制器 push 到 目的地視圖控制器]
??上面的一個代碼段介紹了 push 的方法,在跳轉到 BLSubViewController 的界面之后,我們如果這時需要跳回到剛才那個頁面,其時就是將剛剛押入的棧彈出(所謂的pop方法)。[其實,蘋果已經幫我們在BLSubViewController 的界面設置了跳轉回去的按鈕,如下圖:
現在,我們需要自己來做一個 pop 回去,這樣才算知道所以然.
??首先第一步和前面設置兩個 button 控件一樣的,先在 BLSubViewController 的界面 復制一樣的代碼,改掉相關的代碼,selector 方法名 設置為:backButtonClicked:
“5號”代碼段
- (void)backButtonClicked:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
只方法中的一行代碼就將其移除了。
??值得注意的是在 navigationController 中還為我們提供了另一個可以返回指定 UIViewController 的方法(所以它本身也是一個數組對象 NSArray):
**NSArray popToViewController:(UIViewController ) animated:(BOOL)
還有一個是返回 rootViewController:
NSArray popToRootViewControllerAnimated:(BOOL)
一般是上面代碼段中的方法更加普遍使用,我個人認為比較中庸,但也實在。后面兩個方法則是比較有針對性。
上面我們將文章一開頭的 界面切換 中的第二點 push & pop 講完了,現在來講講第三點 presentModalView。modalView是一個模態窗口(模態的你只能在該窗口進行操作,否則就是非模態窗口),它是蓋在其它視圖之上的。現在我們來完成 2號代碼段 中的 selector :
- (void)presentButtonClicked:(id)sender
{
BLSubViewController *subViewController = [[BLSubViewController alloc] init];
[self presentViewController:subViewController animated:YES completion:nil];
// 第二行代碼中 self presentViewController: 由這個方法可以感知到,presentModalView 確實就是覆蓋其上的一個視圖控制器。
}
設置好之后,跳轉后畫面如下,presentModalView 即圖中右邊視圖。注意 presentViewController:subViewController animated:YES completion:nil 是蘋果的一個新方法,它代替了舊方法,當我們使用新方法的時候需要注意它是支持哪個版本的,根據自己項目的受眾群體進行合理的設置,以免造成使用舊版系統的用戶的應用崩潰。
??左邊顯示的是 push 之后的界面,右邊顯示的是模態窗口。此時,在模態窗口中點擊 back 是不會有任何反應的,因為此時在 BLSubViewController 中的 navigationController 的值是 nil。那么,問題來了,當進入這個模態窗口之后我們點擊 back 是無效的,我們該如何退出這個窗口呢?我們需要使 back 生效,修改 “5號”代碼段 :
“6號”代碼段
- (void)backButtonClicked:(id)sender
{
if (self.navigationController) {
[self.navigationController popViewControllerAnimated:YES];
} else {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
這里使用了 ' dismissViewControllerAnimated: completion: '。至此,點擊模態窗口的 back 按鈕就可以相應并跳轉回原先的界面了。 同時界面切換的第三點也講完了。
接下來,我們需要了解界面切換的第一點 UIViewController 的生命周期。
我們看到先在 BLSubViewController.m 文件中設置這些代碼段:
#pragma mark - Memory management methods
......
#pragma mark - View's lifecycle methods
......
#pragma mark - Custom event methods
......
這樣這個項目中的各個BL...ViewController.m 實現文件中的結構就是由這樣三個代碼塊構成的。在 View's lifecycle methods 中的方法 - (void)viewDidLoad{} 和 - (void)viewDidUnload{} 其實都不止調用一次。例如:
??在上圖中,下面的五個 tabbar 點擊其中一個就跳轉到點擊方的界面,這樣該界面就被 viewDidLoad 了一次。 蘋果的做法很聰明,一般,只有等你點擊了下面其中一個之后,系統才會調用加載那一個視圖控制圖。當出現內存不夠的情況(例子:在點擊并加載第三個視圖的時候用模擬器模擬內存警告的情況),前面調用加載的 one 和 two 視圖 會調用 - (void)didReceiveMemoryWarning {} 方法,接著會調用 - (void)viewDidUnload {} 的方法(
現在這個方法已經不被蘋果使用,當然你可以自己設置
),當調用了這個方法,此方法所在的 試圖控制器就會被釋放(比如先打開了one->two->three, 到 three 出現內存警告,three的圖片不會被釋放,因為用戶正在使用,不過為了內存空間 one & two 就會被釋放)。然后再次點擊 one 或者 two 視圖 ,它們會再次調用 - (void)viewDidLoad {} 的方法。??因此根據上面的理論結合實踐的論述,當在運行app的時候,會根據不同的手機,以及手機不同的內存情況,系統有可能會不止一次的調用 - (void)viewDidLoad {} 和 - (void)viewDidUnload {} 方法。
??不過需要說明的是這個流程只在 iOS 6 之前是存在的。在 iOS 6 之后 - (void)viewDidUnload {} 是被棄用了。不過,我們花這么大的篇幅來介紹,是有助于了解 iOS 的內存是如何進行管理的。
??那現在我們的疑問是,蘋果為什么會棄用這個內存管理方法呢?在聽課的我也帶著這個苦惱,還好蘋果給出了解釋:因為當我們為了內存空間 調用了 - (void)viewDidUnload {} 的方法,是將這個 view 給置 空 了, 但這其時并沒有為內存留出多少空間,這個view所占用的空間其實是很小的,所以便廢棄了。 好吧,似乎是很有道理,不過我也不知道是哪里更占用所謂更多的內存。 不過有一點明確的是,以前在 - (void)viewDidUnload {} 中做的事情,就需要在 - (void)didReceiveMemoryWarning {} 中進行操作。
??在 iOS6 之后,當內存釋放的情況,在這個 viewDidLoad 中的view 并不會被置空,所以我們可以理解為,在那之后 - (void)viewDidLoad {} 方法只會被調用一次。
7 號代碼段
#pragma mark - Memory management methods
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
if (self.view.window == nil) {
}
}
- (void)dealloc
{
}
這之后,當應用收到了內存不夠的警告后,需要在- (void)didReceiveMemoryWarning {}方法中做上述代碼段中的判斷,只有當 self.view.window 等于空的時候,才在里面輸入你要釋放的一些內容(圖片等數據,即同時注意不要將用戶在使用的界面view給釋放了!)。
emsp;上面講的是內存釋放的一些流程,現在接下來再繼續講生命周期的內容,看下面一般完整的生命周期代碼段:
8 號代碼段
#pragma mark - View's lifecycle methods
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
當我們在主界面選擇點擊 push a view 跳轉到 BLSubViewController 后,會先后調用 代碼塊8 的前三個方法,當我們在 BLSubViewController 中點擊 back 按鈕,就會依次調用后兩個方法,并在離開此界面的同時,可能會進行一些釋放或者存儲的功能,這樣還會調用到 7號代碼段的 - (void)dealloc{}方法。
??上述講的就是我們所說的 UIViewController 的生命周期。
這是第一篇完整的使用 Markdown 來寫的一篇筆記,同時一篇認真的筆記的完成確實是很耗費時間的,但在耗費時間的同時也發現,這樣對于理解的深入是很有幫助的。