這裡我們用一個demo來說明ios是如何在視圖控制器之間傳遞重要的參數的。本文先從手寫UI來討論,在下一篇文章中討論在storyboard中傳遞資料。
首先建立一個空工程,並添加一個根視圖控制器類,如所示:
#
在函數didFinishLunchingWithOption中添加幾行代碼,完成後如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; RootViewController *myRootViewController = [[RootViewController alloc] init]; myRootViewController.view.backgroundColor = [UIColor lightGrayColor]; self.window.rootViewController = myRootViewController; return YES;}
完成以後運行,iOS模擬器顯示結果如下:
#
由圖中可以看出,我們自訂的myRootViewController已經成功在啟動時載入,顯示出了一個淺灰色的介面。
隨後如法炮製建立另一個viewController命名為FirstSubViewController,同時在RootViewController的viewDidLoad函數中添加如下代碼:
- (void)viewDidLoad{ [super viewDidLoad];// Do any additional setup after loading the view. UIButton *firstVC = [UIButton buttonWithType:UIButtonTypeSystem]; firstVC.frame = CGRectMake(60, 244, 200, 80); [firstVC setTitle:@"顯示下一個視圖" forState:UIControlStateNormal]; [firstVC addTarget:self action:@selector(displayNextViewController) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:firstVC];}之後還需定義一個函數,名為displayNextViewController,函數體暫時設為空白。此時運行程式,介面中央將顯示一個按鈕“顯示下一個視圖”,但是單擊該按鈕沒有任何反應。這是因為響應函數displayNextViewController還沒有實現。現在在該函數中添加代碼:
- (void)displayNextViewController{ FirstSubViewController *firstSubVC = [[FirstSubViewController alloc] init]; [self presentViewController:firstSubVC animated:YES completion:^{ NSLog(@"present first sub VC ok"); }];}
完成後運行程式發現,按鈕已經有反應了,按下後回出現一個新的白色背景的新介面,這就是我們在這裡定義的firstSubVC;
下一步實現介面的返回操作。在FirstSubViewController的viewDidLoad函數中建立一個回退按鈕,並實現其響應函數。代碼如下:
- (void)viewDidLoad{ [super viewDidLoad];// Do any additional setup after loading the view. UIButton *goBack = [UIButton buttonWithType:UIButtonTypeSystem]; goBack.frame = CGRectMake(60, 244, 200, 80); [goBack setTitle:@"返回上級介面" forState:UIControlStateNormal]; [goBack addTarget:self action:@selector(goBackToPreviousViewController) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:goBack];}- (void)goBackToPreviousViewController{ [self dismissViewControllerAnimated:YES completion:^{ NSLog(@"Back to previous OK"); }];}
到此為止,我們已經通過presentViewController和dismissViewController實現了視圖的切換,下面來考慮兩個視圖控制器之間的資料交流的問題。
在此之前,分別在RootViewController和FirstSubViewController中添加一個標籤和文字框,作為資料的顯示和輸入部分。我們的目的是在FirstSubViewController中輸入一個數字,然後再RootViewController顯示出來。
在這兩個類中分別用property來實現文字框和標籤欄:
//RootViewController.m@interface RootViewController ()@property (strong,nonatomic) UILabel *lable;@end@implementation RootViewController- (UILabel *)lable{ if (!_lable) { _lable= [[UILabel alloc] initWithFrame:CGRectMake(60, 150, 200, 30)]; _lable.textAlignment = NSTextAlignmentCenter; _lable.text = @"Hello World!"; _lable.backgroundColor = [UIColor whiteColor]; } return _lable;}…..@end//FirstSubViewController@interface FirstSubViewController ()@property (strong,nonatomic) UITextField *inputText;@end@implementation FirstSubViewController- (UITextField *)inputText{ if (!_inputText) { _inputText = [[UITextField alloc] initWithFrame:CGRectMake(60, 150, 200, 30)]; _inputText.backgroundColor = [UIColor lightGrayColor]; } return _inputText;}…..@end
在這兩個視圖控制器的viewDidLoad中分別添加如下代碼:
//RootViewController.m- (void)viewDidLoad{ [super viewDidLoad];// Do any additional setup after loading the view. [self.view addSubview:self.label]; UIButton *firstVC = [UIButton buttonWithType:UIButtonTypeSystem]; …..}…..//FirstSubViewController- (void)viewDidLoad{ [super viewDidLoad];// Do any additional setup after loading the view. //添加輸入框 [self.view addSubview:self.inputText]; UIButton *goBack = [UIButton buttonWithType:UIButtonTypeSystem]; …..}
在視圖控制器之間進行資料傳遞可以通過多種方法,下面將逐個實驗:
1、使用代理delegate的方法:
基本原理:FirstSubViewControllers試圖改變RootViewController的資料,但是除了操作自己內部的資料的能力之外,不能改變其他類的資料。想要將這些資料傳遞出去,需要設定代理方法,擷取FirstSubViewControllers中資料的類來遵循該方法,通過實現該代理協議中的方法來擷取FirstSubViewControllers中的資料。
具體步驟:
首先在FirstSubViewController.h中定義協議:
@protocol FirstSubViewControllerDelegate @optional- (void)getStringFromFirstSubViewControllerDelegate:(NSString *)outputString;@end
然後添加一個符合該協議的代理屬性:
@property (nonatomic,weak) id delegate;
在RootViewControlller.h中,聲明該類符合FirstSubViewControllerDelegate協議:
@interface RootViewController : UIViewController
在建立FirstSubViewController的執行個體後,將其delegate屬性定義為self,同時實現協議中的方法,兩個函數如下:
- (void)getStringFromFirstSubViewControllerDelegate:(NSString *)outputString{ self.lable.text = outputString;}- (void)displayNextViewController{ FirstSubViewController *firstSubVC = [[FirstSubViewController alloc] init]; firstSubVC.delegate = self; [self presentViewController:firstSubVC animated:YES completion:^{ NSLog(@"present first sub VC ok"); }];}
最後在FirstSubViewController.m的goBackToPreviousViewController中添加一句向delegate屬性發送擷取資料的訊息:
- (void)goBackToPreviousViewController{ [self.delegate getStringFromFirstSubViewControllerDelegate:self.inputText.text]; [self dismissViewControllerAnimated:YES completion:^{ NSLog(@"Back to previous OK"); }];}
這樣,大功告成了,在FirstSubViewController中的輸入框中輸入的內容,在按返回按鈕後回顯示在第一個介面的標籤上。
2、使用通知Notification的方法
關於通知的知識在未來將會詳述,在這裡只是簡要介紹一種使用通知這一機制的方法。
在使用通知之前,必須在預設通知中樞裡添加一個所謂“觀察者”和通知,這個通知是命名的,同時還指定了回調的方法。當該通知中樞收到了某個對象發送了相應的通知時,將會調用指定的方法執行某項操作。通知的寄件者同時還可以發送相應的訊息作為通知的參數。
註冊通知中樞可以在RootViewController.m中的viewDidLoad函數的末尾添加如下函數:
//使用通知的方式實現[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeLabelText:) name:@"ChangeLabelTextNotification" object:Nil];
此外還要實現觀察者的回呼函數:
- (void)changeLabelText:(NSNotification *)notification{ id text = notification.object; _lable.text = text;}自此通知的接收端已經完成。
通知的發送端,只需在“返回上級介面”的相應函數中按照事先定義的名稱發送通知和參數即可:
//使用通知方式實現 [[NSNotificationCenter defaultCenter] postNotificationName:@"ChangeLabelTextNotification" object:_inputText.text];
此時運行,將會發現和代理模式起到了相同的作用。
除了代理和通知之外,還有其他如KVO等方法,未來還會詳細探討。