- iPhone UIKit詳解
- 王志剛 王中元 朱蕾編著
- 3720字
- 2019-01-01 07:04:26
3.2 畫面跳轉(zhuǎn)
3.2.1 使用UITabBarController實(shí)現(xiàn)并列畫面跳轉(zhuǎn)
前一節(jié)介紹了由UIViewController實(shí)現(xiàn)的畫面切換。實(shí)際上并非真的實(shí)現(xiàn)了兩個(gè)畫面間的跳轉(zhuǎn),而是同時(shí)啟動(dòng)兩個(gè)畫面,控制其中哪一個(gè)畫面顯示在前臺(tái),哪一個(gè)畫面顯示在后臺(tái)而已。這種畫面跳轉(zhuǎn)方式有一個(gè)很大的缺點(diǎn),即當(dāng)畫面數(shù)量增加時(shí),畫面跳轉(zhuǎn)的實(shí)現(xiàn)代碼將越來(lái)越復(fù)雜,而且各個(gè)畫面間不可避免地有相互依賴關(guān)系。
實(shí)際上,在UIKit中提供了專用的管理畫面跳轉(zhuǎn)的類,這就是UITabBarController類以及UINavigationController類。本節(jié)首先介紹使用UITabBarController類如何實(shí)現(xiàn)畫面間的跳轉(zhuǎn)功能。
下面我們以上一節(jié)實(shí)例代碼為基礎(chǔ),將其改造成使用UITabBarController類來(lái)實(shí)現(xiàn)畫面間的跳轉(zhuǎn)。首先將HelloWorldAppDelegate改名為MultiViewAppDelegate,并進(jìn)行如下修改(代碼中粗體字部分)。
[MultiViewAppDelegate.h] #import <UIKit/UIKit.h> @interface MultiViewAppDelegate :NSObject <UIApplicationDelegate> { UIWindow *window; UIViewController *rootController; } @property(nonatomic,retain)UIWindow *window; @end [MultiViewAppDelegate.m] #import "MultiViewAppDelegate.h" #import "ViewController1.h" #import "ViewController2.h" @implementation MultiViewAppDelegate @synthesize window; #pragma mark - #pragma mark Application lifecycle -(BOOL)application:(UIApplication *)application didFinishLaunchin gWithOptions:(NSDictionary *)launchOptions { // 初始化window實(shí)例變量 CGRect frame = [[UIScreen mainScreen] bounds]; self.window = [[UIWindow alloc] initWithFrame:frame]; // 創(chuàng)建母體Controller實(shí)例 rootController = [[UITabBarController alloc] init]; // 創(chuàng)建畫面1與畫面2的Controller實(shí)例 ViewController1 *tab1 = [[[ViewController1 alloc] init] autorelease]; ViewController2 *tab2 = [[[ViewController2 alloc] init] autorelease]; // 將畫面1、畫面2的Controller實(shí)例以數(shù)組的形式追加到母體Controller中 NSArray *tabs = [NSArray arrayWithObjects:tab1,tab2,nil]; [(UITabBarController)rootController setViewControllers:tabs animated:NO]; // 將母體Controller的view追加到Window中 [self.window addSubView:rootController.view]; [self.window makeKeyAndVisible]; return YES; } -(void)dealloc { [rootController release]; [window release]; [super dealloc]; } @end
要修改的地方并不多。首先刪除前例中單獨(dú)創(chuàng)建兩個(gè)畫面的實(shí)例變量ViewControllerl以及ViewController2,取而代之的是UITabBarController類型的實(shí)例變量rootController。上例中將兩個(gè)畫面的view直接追加到Window中,本例中只需要將UITabBarController類型實(shí)例變量的view追加到Window中。而將兩個(gè)畫面的UIViewController對(duì)象以數(shù)組的形式追加到UITabBarController類型的實(shí)例變量中,此時(shí)調(diào)用了setViewControllers:animated:方法,此方法的第一個(gè)參數(shù)即為UIViewController對(duì)象數(shù)組。
接著,我們開始修改兩個(gè)畫面的實(shí)例代碼。其實(shí)施方法如下。
- 按鈕去掉,刪除與按鈕相關(guān)的代碼。
- 重寫(或稱覆蓋)init方法,其中追加與標(biāo)簽相關(guān)的代碼。
兩個(gè)畫面中的init方法的代碼如下。
[ViewController1.m] -(id)init { if((self = [super init])){ // 設(shè)置tabBar的相關(guān)屬性 self.title = @"Hello"; UIImage*icon = [UIImage imageNamed:@"ball1.png"]; self.tabBarItem = [[[UITabBarItem alloc] initWithTitle:@"Hello" image:icon tag:0] autorelease]; } return self; } [ViewController2.m] -(id)init { if((self = [super init])){ // 設(shè)置tabBar的相關(guān)屬性 self.title = @"您好"; UIImage*icon = [UIImage imageNamed:@"ball2.png"]; self.tabBarItem = [[[UITabBarItem alloc] initWithTitle:@"您好" image:icon tag:0] autorelease]; } return self; }
重寫的init方法中也沒(méi)有追加多少內(nèi)容。設(shè)置title屬性后,導(dǎo)入圖標(biāo)用的圖片,并將其設(shè)置到UIViewController的 tabBarItem屬性中。調(diào)用initWithTitle:image:tag:方法進(jìn)行UITabBarItem類的初始化。第一個(gè)參數(shù)為標(biāo)簽條中顯示的標(biāo)題;第二個(gè)參數(shù)為指定顯示的圖標(biāo)圖片;第三個(gè)參數(shù)為標(biāo)簽的序號(hào),此序號(hào)通常用于程序內(nèi)檢索。
執(zhí)行這個(gè)經(jīng)過(guò)改造的工程后,將顯示如圖3-3所示的結(jié)果。

圖3-3 標(biāo)簽實(shí)現(xiàn)的畫面跳轉(zhuǎn)
3.2.2 使用UINavigationController實(shí)現(xiàn)多層畫面跳轉(zhuǎn)
iPhone4手機(jī)的自帶應(yīng)用程序中,既有使用UITabBarController來(lái)進(jìn)行畫面切換控制的,也有使用UINavigationController來(lái)實(shí)現(xiàn)多畫面間的跳轉(zhuǎn)的。例如iPod音樂(lè)播放界面就采用了UITabBarController來(lái)進(jìn)行畫面切換控制,而iPhone手機(jī)設(shè)置程序則采用了UINavigationController來(lái)實(shí)現(xiàn)多層次畫面間的跳轉(zhuǎn),圖3-4、圖3-5是這兩個(gè)程序部分畫面的跳轉(zhuǎn)示意圖。

圖3-4 iPod音樂(lè)播放程序畫面跳轉(zhuǎn)示意圖
與UITabBarController實(shí)現(xiàn)畫面并行切換形式不同,UINavigationController是實(shí)現(xiàn)畫面多層次跳轉(zhuǎn),也是其最大的特征。如圖3-5所示,畫面1-1可以跳轉(zhuǎn)至其下一層的畫面1-1-1以及畫面1-1-2中。另外UINavigationController可以自動(dòng)地記憶跳轉(zhuǎn)所經(jīng)過(guò)的路徑,按照這些記錄的路徑信息,可以依次返回到上層畫面中(即支持返回按鈕)。

圖3-5 iPhone設(shè)置程序部分畫面跳轉(zhuǎn)示意圖
下面我們將上一節(jié)采用UITabBarController實(shí)現(xiàn)的畫面切換程序,再一次改造成采用UINavigationController來(lái)實(shí)現(xiàn)畫面的跳轉(zhuǎn)程序。其中兩個(gè)下一層的畫面保持不變,在這之前我們還將追加一個(gè)主畫面,主畫面非常簡(jiǎn)單,只有一個(gè)iPhone表格視圖組成,兩個(gè)下層畫面的名稱依次顯示在表格中,當(dāng)單擊任何一個(gè)名稱時(shí)將會(huì)跳轉(zhuǎn)到對(duì)應(yīng)的下層畫面中,整個(gè)應(yīng)用程序的跳轉(zhuǎn)示意圖如圖3-6所示。

圖3-6 改造目標(biāo)程序的跳轉(zhuǎn)示意圖
要實(shí)現(xiàn)上述程序,首先要進(jìn)行如下兩處修改。
● 在HelloWorldAppDelegate.m中將基準(zhǔn)ViewController由UITableViewController替換為UINavigationController。
● 新追加主畫面的TopMenuController類。
需要注意一點(diǎn),這里我們對(duì)畫面1以及畫面2的實(shí)現(xiàn)代碼其實(shí)是沒(méi)有進(jìn)行任何修改的。只是留下了些原實(shí)例中追加的關(guān)于UITabBarController的注釋,請(qǐng)務(wù)必忽略。
下面我們看看HelloWorldAppDelegate.m的修改代碼,見代碼中的黑體字部分。
[HelloWorldAppDelegate.m] #import "HelloWorldAppDelegate.h" #import "TopMenuController.h" @implementation HelloWorldAppDelegate @synthesize window = window_; -(void)applicationDidFinishLaunching:(UIApplication *)application { // 初始化Window實(shí)例變量 CGRect bounds = [[UIScreen mainScreen] bounds]; window_ = [[UIWindow alloc] initWithFrame:bounds]; // 創(chuàng)建基準(zhǔn)的Controller對(duì)象 TopMenuController*topMenu = [[[TopMenuController alloc] init] autorelease]; rootController_ = [[UINavigationController alloc] initWithRootVi ewController:topMenu]; // 將主畫面的view追加到Window中 [window_ addSubview:rootController_.view]; [window_ makeKeyAndVisible]; } -(void)dealloc { [rootController_ release]; [window_ release]; [super dealloc]; } @end
大家可以看到,其實(shí)只修改了其中三行代碼。首先需要導(dǎo)入 TopMenuController類,并創(chuàng)建其實(shí)例。接著初始化UINavigationController,需要向initWithRootViewController:方法中傳入根畫面的Controller,這里將創(chuàng)建好的TopMenuController實(shí)例傳入。然后將UINavigationController的view屬性追加到UIWindow中(使用addSubView:方法)。
下面是新追加的主菜單畫面TopMenuController的代碼。這里將TopMenuController以UITableViewController子類形式來(lái)創(chuàng)建。
[TopMenuController.h] #import <UIKit/UIKit.h> @interface TopMenuController :UITableViewController { @private NSMutableArray* items_; } @end [TopMenuController.m] #import "TopMenuController.h" @implementation TopMenuController -(void)dealloc { [items_ release]; [super dealloc]; } -(id)init { if((self = [super initWithStyle:UITableViewStylePlain])){ self.title = @"主菜單"; //-------------------------------<1> // 創(chuàng)建顯示用數(shù)組 items_ = [[NSMutableArray alloc] initWithObjects: @"ViewController1", @"ViewController2", nil ]; } return self; } #pragma mark ----- UITableViewDataSource Methods ----- -(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section { return [items_ count]; } -(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { // 檢查單元是否已經(jīng)創(chuàng)建 UITableViewCell* cell = [tableView dequeueReusableCellWithIdenti fier:@"simple-cell"]; if(!cell){ // 沒(méi)有創(chuàng)建的單元新創(chuàng)建 cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"simple-cell"] autorelease]; } // 設(shè)置單元中顯示的文本字符串 cell.textLabel.text = [items_ objectAtIndex:indexPath.row]; //------<2> return cell; } #pragma mark ----- UITableViewDelegate Methods ---------<3> -(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath { Class class = NSClassFromString([items_ objectAtIndex:indexPath.row]); id viewController = [[[class alloc] init] autorelease]; if(viewController){[self.navigationController pushViewController:viewController animated:YES]; } } @end
與表相關(guān)的解說(shuō)將放在第7章中,代碼中有不理解的地方,請(qǐng)參考第7章的相關(guān)介紹。首先看看init方法中的內(nèi)容。在titile屬性中設(shè)置畫面的標(biāo)題(<1>)。使用UINavigationController時(shí),此處設(shè)置的標(biāo)題將在畫面的最上方中心位置顯示(見圖3-7)。接著還創(chuàng)建了NSArray數(shù)組,NSArray數(shù)組中的元素將顯示在表中。具體處理在tableView:cellForRowAtIndexPath:方法中,將NSArray中的元素設(shè)置到表的單元中。

圖3-7 UINavigationController的標(biāo)題
其次,我們?cè)俅_認(rèn)一下tableView:didSelectRowAtIndexPath:方法的處理內(nèi)容。此方法將在表單元被觸摸(單擊)時(shí)調(diào)用,參數(shù)indexPath中保存了具體被觸摸(單擊)的行信息。
本例中,表單元中直接放置了跳轉(zhuǎn)對(duì)象畫面的類名,tableView:didSelectRow AtIndexPath:方法中首先創(chuàng)建被觸摸行類的實(shí)例。然后跳轉(zhuǎn)到對(duì)應(yīng)畫面中。調(diào)用UINavigationController的pushViewController:animated:方法實(shí)現(xiàn)畫面跳轉(zhuǎn);向此方法的第一個(gè)參數(shù)中傳入畫面(UIViewController)的實(shí)例后,然后就會(huì)自動(dòng)跳轉(zhuǎn)到對(duì)應(yīng)層次的畫面中。另外如果將animated參數(shù)設(shè)置為YES,則下一畫面將以動(dòng)畫的形式顯示出來(lái)。此時(shí)UINavigationController實(shí)例可以通過(guò)UIViewController的navigationController屬性獲取。只要是UINavigationController管理下的UIViewController,隨時(shí)都可以通過(guò)其navigationController屬性獲取UINavigationController實(shí)例本身。
這樣就算完成了整個(gè)層次的畫面跳轉(zhuǎn)應(yīng)用程序。跳轉(zhuǎn)到下一層畫面后,將自動(dòng)顯示如圖3-8所示的返回按鈕。

圖3-8 UINavigationController的返回按鈕
3.2.3 跳轉(zhuǎn)到任意畫面
上一小節(jié)中通過(guò)pushViewController:animated:方法能實(shí)現(xiàn)畫面的跳轉(zhuǎn),而且能在導(dǎo)航條上自動(dòng)追加返回上一畫面的返回按鈕。這種“返回到前一畫面”的功能正確的表述應(yīng)該為“返回到上一級(jí)”畫面,調(diào)用popViewControllerAnimation:方法也能實(shí)現(xiàn)同樣的功能。其他的如UINavigationController類還提供了直接返回到主畫面的popToRootViewControllerAnimation:方法,以及返回到任意一級(jí)畫面的popToViewController:animated:方法,以下是這三種方法的調(diào)用代碼。
// 返回到上一級(jí)畫面 [self.navigationController popViewControllerAnimation:Yes]; // 返回根畫面 [self.navigationController popToRootViewControllerAnimation:Yes]; // 返回任意指定畫面 [self.navigationController popToViewController:viewController animated:Yes];
另外,從iPhone OS 3.0以后,可以通過(guò)調(diào)用setViewController:animated:方法將畫面的跳轉(zhuǎn)歷史路徑(堆棧)完全替換。替換歷史路徑的示意圖如圖3-9所示。

圖3-9 替換歷史路徑
實(shí)現(xiàn)圖3-9所示的替換歷史路徑的代碼如下。
// 將跳轉(zhuǎn)歷史路徑替換為畫面1> 畫面1-3> 畫面1-3-1 id scene1 = [[[Scene alloc] init] autorelease]; // 創(chuàng)建畫面1的實(shí)例 id scene13 = [[[Scene3 alloc] init] autorelease]; // 創(chuàng)建畫面1-3的實(shí)例 id scene131 = [[[Scene31 alloc] init] autorelease]; // 創(chuàng)建畫面1-3-1的實(shí)例 NSArray *history = [NSArray arrayWithObjects:scene1,scene13,scene13 1,nil]; [self.navigationController setViewControllers:history animated:Yes];
popToViewController:animated:方法的第一個(gè)參數(shù)中必須傳入U(xiǎn)IViewController的實(shí)例,不是新創(chuàng)建的實(shí)例,而是實(shí)際跳轉(zhuǎn)過(guò)程中原畫面的實(shí)例。此時(shí),可以通過(guò)UINavigationController的viewControllers屬性來(lái)參照。viewControllers屬性中保存的正是NSArray形式的跳轉(zhuǎn)畫面實(shí)例集合。
調(diào)用setViewControllers:animated方法進(jìn)行跳轉(zhuǎn)畫面堆棧替換時(shí),也可以viewControllers屬性中保存的實(shí)例集合為基礎(chǔ),進(jìn)行部分UIViewController實(shí)例的替換。
3.2.4 模態(tài)(modal)畫面的顯示方法
PC桌面軟件中經(jīng)常可以看到如“文件讀取對(duì)話框”等模態(tài)對(duì)話框的畫面類型。這些畫面就顯示在主畫面的上方,當(dāng)對(duì)話框中的操作結(jié)束,關(guān)閉對(duì)話框畫面后將顯示原來(lái)的畫面,屬于一種臨時(shí)畫面。iPhone應(yīng)用程序中也能實(shí)現(xiàn)這種模態(tài)畫面,例如iPhone通信錄管理程序中,追加新的通信錄時(shí)也使用了這種模態(tài)畫面。
模態(tài)畫面沒(méi)有什么特別的地方,與其他畫面一樣也是由UIViewController的子類實(shí)現(xiàn)的畫面,只是調(diào)用的方式不同而已。以下是模態(tài)畫面顯示的調(diào)用方式以及顯示后關(guān)閉畫面的實(shí)例代碼。
// ModalDialog為UIViewController的子類 id dialog = [[[ModalDialog alloc] init] autorelease]; [self presentModalViewController:dialog animated:YES]; // 關(guān)閉模態(tài)UIViewController [self dismissModalViewControllerAnimationed:YES];
如上述代碼所示,將UIViewController子類的實(shí)例作為presentModalViewController:animated:方法的第一個(gè)參數(shù)進(jìn)行調(diào)用后,就能實(shí)現(xiàn)以模態(tài)方式顯示畫面。關(guān)閉時(shí)調(diào)用dismissModalViewControllerAnimationed:方法。模態(tài)畫面調(diào)用后的示意圖如圖3-10所示。

圖3-10 模態(tài)畫面顯示示意圖
從iPhone OS 3.0開始,追加了設(shè)置模態(tài)畫面顯示/隱藏時(shí)動(dòng)畫效果的modalTranstionStyle屬性,可設(shè)置三種不同的值,分別如下。
● UIModalTransitionStyleCoverVertical:畫面從下向上徐徐彈出,關(guān)閉時(shí)向下隱藏(默認(rèn)方式)。
● UIModalTransitionStyleFlipHorizontal:從前一個(gè)畫面的后方,以水平旋轉(zhuǎn)的方式顯示后一畫面。
● UIModalTransitionStyleCrossDissolve:前一畫面逐漸消失的同時(shí),后一畫面逐漸顯示。
以下是圖3-10所示模態(tài)畫面的代碼,僅供參考。大家可以看到,其與普通的UIViewController子類沒(méi)有任何區(qū)別。
// 以模態(tài)形式顯示的畫面 // 內(nèi)容與普通畫面一樣 @interface ModalDialog :UIViewController @end @implementation ModalDialog -(void)viewDidLoad { [super viewDidLoad]; // 追加1個(gè)標(biāo)簽 UILabel* label = [[[UILabel alloc] initWithFrame:self.view.bounds] autorelease]; label.backgroundColor = [UIColor blackColor]; label.textColor = [UIColor whiteColor]; label.textAlignment = UITextAlignmentCenter; label.text = @"您好。我是模態(tài)畫面。"; [self.view addSubview:label]; // 追加關(guān)閉按鈕 UIButton* goodbyeButton = [UIButton buttonWithType:UIButtonTypeR oundedRect]; [goodbyeButton setTitle:@"Good-bye" forState:UIControlStateNormal]; [goodbyeButton sizeToFit]; CGPoint newPoint = self.view.center; newPoint.y += 80; goodbyeButton.center = newPoint; [goodbyeButton addTarget:self action:@selector(goodbyeDidPush) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:goodbyeButton]; } -(void)goodbyeDidPush { // 關(guān)閉模態(tài)對(duì)話框 [self dismissModalViewControllerAnimated:YES]; } @end
- 構(gòu)建運(yùn)營(yíng)級(jí)IPv6網(wǎng)絡(luò)
- 用萬(wàn)用表檢修液晶電視機(jī)一學(xué)就會(huì)
- 西門子SINAMICS G120/S120變頻器技術(shù)與應(yīng)用
- 基于數(shù)字條紋投影的在線深度獲取技術(shù)
- 移動(dòng)網(wǎng)絡(luò)安全體系架構(gòu)與防護(hù)技術(shù)
- 路由與交換技術(shù)
- 嵌入式Linux網(wǎng)絡(luò)體系結(jié)構(gòu)設(shè)計(jì)與TCP/IP協(xié)議棧
- 常用電子元器件識(shí)別/檢測(cè)/選用一讀通(第2版)
- 微波射頻電路設(shè)計(jì)與仿真100例
- 光波分復(fù)用系統(tǒng)與維護(hù)
- 廈華/海爾新型彩色電視機(jī)速修圖解
- 現(xiàn)代電子產(chǎn)品工藝
- 電子裝配工技能實(shí)訓(xùn)與考核指導(dǎo)(中、高級(jí)工)
- 無(wú)源RFID電子標(biāo)簽天線理論與工程 (清華開發(fā)者書庫(kù))
- 移動(dòng)通信天線技術(shù)與工程應(yīng)用