在任何的軟件開(kāi)發(fā)中都離不開(kāi)界面與界面之間的通信,界面通信的最直接的方法就是界面?zhèn)髦?
在開(kāi)發(fā)過(guò)程中我們?cè)陧?yè)面?zhèn)髦禃r(shí)我們通常使用的方法有:屬性傳值法,block傳值法,代理傳值法,以及單例傳值法,通知傳值法
屬性傳值
屬性傳值多用于在將前一個(gè)頁(yè)面的值傳到后一個(gè)頁(yè)面去,也就是我們通常說(shuō)的從前往后傳值
當(dāng)?shù)谝粋€(gè)頁(yè)面push到第二個(gè)頁(yè)面的時(shí)候,我們?cè)诘诙€(gè)頁(yè)面聲明一個(gè)屬性用于接受從第一個(gè)頁(yè)面?zhèn)鬟f過(guò)去的值,然后在push這個(gè)事件被觸發(fā)的時(shí)候進(jìn)行賦值,也就是說(shuō),先初始化創(chuàng)建第二個(gè)控制器(頁(yè)面),然后通過(guò)創(chuàng)建的控制器來(lái)訪問(wèn)它所對(duì)應(yīng)的屬性,將即將傳遞的值賦給它,這樣就完成了屬性傳值.于是當(dāng)頁(yè)面(控制器)被push到第二個(gè)頁(yè)面之后我們?cè)L問(wèn)它的屬性的時(shí)候,也就順便獲取到了傳遞過(guò)來(lái)的值.
例如:現(xiàn)在有兩個(gè)控制器FirstViewController和SecondViewController.我們?cè)贔irstViewController中創(chuàng)建一個(gè)UITextFiled,在SecondViewController中創(chuàng)建一個(gè)UILabel,然后在textField中輸入文字,我們使用導(dǎo)航控制器來(lái)控制頁(yè)面之間的跳轉(zhuǎn),當(dāng)我們跳轉(zhuǎn)到第二個(gè)頁(yè)面的時(shí)候,將在textField中輸入的文字傳遞給label,作為label上的文字.
代碼如下:
FirstViewController中:
self.navigationItem.title = @"First";
// 設(shè)置控制器的背景顏色
self.view.backgroundColor = [UIColor yellowColor];
// 設(shè)置控制器的右按鈕,并創(chuàng)建事件點(diǎn)擊調(diào)到下一頁(yè)面
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"第二頁(yè)" style:UIBarButtonItemStyleDone target:self action:@selector(next:)];
// 創(chuàng)建輸入框
self.firstTF = [[UITextField alloc] initWithFrame:CGRectMake(50, 100, 314, 40)];
self.firstTF.placeholder = @"請(qǐng)輸入文字!";
_firstTF.borderStyle = UITextBorderStyleLine;
[self.view addSubview:_firstTF];
SecondViewController中:
self.navigationItem.title = @"Second";
self.view.backgroundColor = [UIColor cyanColor];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStyleDone target:self action:@selector(back:)];
self.secondLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 314, 40)];
self.secondLabel.backgroundColor = [UIColor orangeColor];
// 由上一個(gè)頁(yè)面通過(guò)屬性傳值傳過(guò)來(lái)
self.secondLabel.text = _tfString;
[self.view addSubview:_secondLabel];
我們?cè)赟econdViewController的.h文件中設(shè)置一個(gè)字符串屬性用來(lái)接收從FirstViewController中傳遞過(guò)來(lái)的值.
@property(nonatomic,strong)NSString *tfString;
那么我們?cè)谑裁磿r(shí)候傳遞這個(gè)值呢?
合適的地方應(yīng)該是在第一個(gè)頁(yè)面跳轉(zhuǎn)到第二個(gè)頁(yè)面的時(shí)候創(chuàng)建,也就是在push方法執(zhí)行之前進(jìn)行傳值.我們?cè)贔irsrViewController的右按鈕點(diǎn)擊事件也就是next方法中進(jìn)行傳值.
-(void)next:(UINavigationController *)sender{
ScondViewController *secondNC = [[ScondViewController alloc] init];
// 將第一個(gè)界面的輸入框信息賦值給第二個(gè)頁(yè)面的Label
secondNC.tfString = _firstTF.text;
[self.navigationController pushViewController:secondNC animated:YES];
}
這樣就實(shí)現(xiàn)了從前往后傳值,也就是屬性傳值
代理傳值
代理傳值多用于從后往前傳值.
我們還是使用上面的額例子,比如我們現(xiàn)在在SecondViewController中進(jìn)行的相關(guān)的操作,現(xiàn)在我們要返回到第一個(gè)頁(yè)面,但是我們現(xiàn)在有一個(gè)需求就是需要在SecondViewController中創(chuàng)建一個(gè)UITextField,在FirstViewController中創(chuàng)建一個(gè)UILabel,當(dāng)頁(yè)面pop回第一個(gè)頁(yè)面的時(shí)候?qū)econdViewController中textField中的文字傳遞到FirstViewController中的label上,這個(gè)時(shí)候,我們就可以使用代理傳值.
我們?cè)赟econdViewController的.h中創(chuàng)建一個(gè)協(xié)議,并且設(shè)置代理屬性,讓其遵循該協(xié)議.
代碼如下:
// 設(shè)置一個(gè)協(xié)議方法
@protocol SecondVCDelegate
// 代理傳值
- (void)passValue:(NSString *)value;
@end
// 聲明代理屬性進(jìn)行代理傳值
@property(nonatomic,weak)id delegate;
在SecondViewController中創(chuàng)建textField
self.secondTF = [[UITextField alloc] initWithFrame:CGRectMake(50, 200, 314, 40)];
self.secondTF.placeholder = @"請(qǐng)輸入";
self.secondTF.borderStyle = UITextBorderStyleLine;
[self.view addSubview:_secondTF];
在FirstViewController中創(chuàng)建label并讓其遵循協(xié)議
#import "FirstViewController.h"
#import "ScondViewController.h"
@interface FirstViewController ()
@property(nonatomic,strong)UILabel *firstLab;
@end
創(chuàng)建UILabel
self.firstLab = [[UILabel alloc] initWithFrame:CGRectMake(50, 200, 314, 40)];
self.firstLab.backgroundColor = [UIColor magentaColor];
[self.view addSubview:_firstLab];
我們?cè)谏鲜鰧傩詡髦荡a中已經(jīng)為SecondViewController的導(dǎo)航控制器設(shè)置了右按鈕(返回按鈕),那么,我們只需要在其觸發(fā)事件(back:)中讓代理去執(zhí)行傳值即可.
代碼如下:
- (void)back:(UINavigationController *)sender{
// 代理去執(zhí)行傳值
[_delegate passValue:_secondTF.text];
[self.navigationController popViewControllerAnimated:YES];
}
然后在FirstViewController的next方法中為SecondNC制定其代理為其自身即可.
實(shí)現(xiàn)其代理方法
- (void)passValue:(NSString *)value{
_firstLab.text = value;
}
這樣我們就可以在第一個(gè)頁(yè)面得到第二個(gè)頁(yè)面的值了.
block傳值
block的本質(zhì)就和其他OC中變量類似,只不過(guò),block中存儲(chǔ)的數(shù)據(jù)是函數(shù)體,但是在使用block時(shí)完全可以像調(diào)用其他函數(shù)似的,傳入?yún)?shù),然后得到返回值.關(guān)于詳細(xì)的block,在這里就不贅述了.
在iOS開(kāi)發(fā)中我們用到block進(jìn)行傳值的情況多數(shù)情況下也是在從后往前傳值.所以我們依然使用上述例子.
使用block傳值,首先我們需要在SecondViewController中定義并聲明block屬性.
// 定義有參無(wú)返回值的匿名函數(shù)(傳遞字符串)
typedef void(^MyBlock)(NSString *);
@interface SecondViewController : UIViewController
@property(nonatomic,copy)MyBlock block;
@end
同上,我們將SecondViewController中textField中輸入的文字傳遞到FirstViewController中的label上顯示.
我們?cè)赟econdViewController的back方法中調(diào)用block,并且將在這個(gè)控制器的textField中的文字作為block的參數(shù)傳遞給block.
- (void)back:(UINavigationController *)sender{
// 代理去執(zhí)行傳值
self.block(_secondTF.text);
[self.navigationController popViewControllerAnimated:YES];
}
然后我們?cè)贔irstViewController的next方法中,也就是alloc出SecondViewController的時(shí)候調(diào)用SecondViewController的block,實(shí)現(xiàn)傳值
- (void)back:(UINavigationController *)sender{
__weak typeof(self)temp = self;
secVC.block = ^(NSString *string){
// 通過(guò)回調(diào)將傳進(jìn)來(lái)的字符串賦值給label
temp.firstLab.text = string;
};
[self.navigationController popViewControllerAnimated:YES];
}
在上述代碼中,因?yàn)閎lock里面不能直接使用屬性,實(shí)例變量和方法(因?yàn)闀?huì)造成循環(huán)引用),所以我們重新用__weak修飾self并重新命名為temp.這樣我們就實(shí)現(xiàn)了傳值.
單例傳值
由于單例在內(nèi)存中只創(chuàng)建一次的并且可以全局訪問(wèn)的屬性,我們可以在必要的時(shí)候?qū)?shù)據(jù)存放在單例的屬性中,并且在必要的時(shí)候從單例中通過(guò)訪問(wèn)其屬性進(jìn)行調(diào)用,這樣就實(shí)現(xiàn)值的傳遞
首先我們先創(chuàng)建一個(gè)單例類DataHandle,在其.h文件中我們使用類方法聲明單例創(chuàng)建的方法,并且聲明一個(gè)字符串屬性用來(lái)保存值,(因?yàn)槲覀円廊皇褂蒙鲜龅睦?所以聲明字符串,來(lái)保存textField中輸入的文字,在實(shí)際的開(kāi)發(fā)過(guò)程中,讀者可以自己根據(jù)實(shí)際情況而定).
@interface DataHandle : NSObject
// 創(chuàng)建單例
+ (instancetype)sharedDataHandle;
@property(nonatomic,strong)NSString *Str;
@end
在.m文件中實(shí)現(xiàn)其初始化方法
@implementation DataHandle
// 聲明靜態(tài)區(qū)對(duì)象的原因,希望程序運(yùn)行期間,在內(nèi)存中一直存在,這樣對(duì)外界來(lái)說(shuō),可以隨時(shí)讀取數(shù)據(jù)
static DataHandle *dataHandle = nil;
// 創(chuàng)建單例(全局區(qū))
+ (instancetype)sharedDataHandle{
if (nil == dataHandle) {
// 我們創(chuàng)建單例使用加號(hào)方法的原因是因?yàn)?在創(chuàng)建之前,無(wú)法存在一個(gè)實(shí)例對(duì)象去調(diào)用動(dòng)態(tài)方法來(lái)創(chuàng)建它本身
dataHandle = [[DataHandle alloc] init];
}
return dataHandle;
}
@end
無(wú)論是從FirstViewController中將textField的值傳到SecondViewController的label上還是反過(guò)來(lái)傳值.我們只需要在需要的時(shí)候通過(guò)其類方法(+ (instancetype)sharedDataHandle)來(lái)創(chuàng)建出單例對(duì)象來(lái),然后將textField的text屬性以賦值的方式賦給單例的Srt屬性即可
代碼如下:
-(void)next:(UINavigationController *)sender{
DataHandle *dataHandle = [DataHandle sharedDataHandle];
ScondViewController *secondNC = [[ScondViewController alloc] init];
// 將第一個(gè)界面的輸入框信息賦值給第二個(gè)頁(yè)面的Label
dataHandle.Str = _firstTF.text;
[self.navigationController pushViewController:secondNC animated:YES];
}
假使在SecondViewController中我們?yōu)閘abel的text賦值,而其值就是FirstViewController中在textField中輸入的值,那么我們可以這樣寫(xiě):
DataHandle *dataHandle = [DataHandle sharedDataHandle];
self.secondLabel.text = dataHandle.str;
如上所述就實(shí)現(xiàn)了值的傳遞.
其實(shí)單例不知是在兩個(gè)頁(yè)面之間進(jìn)行傳遞,由于單例的一次創(chuàng)建全局訪問(wèn)的特點(diǎn),我們可以將值傳遞到ThreeViewController,FourViewController等等的控制器,相反的也可以反向傳遞,在此就不在重復(fù)了,其實(shí)原理都是相同的.
通知傳值
通知在iOS開(kāi)發(fā)中占據(jù)著非常重要的地位,通常在兩個(gè)頁(yè)面關(guān)聯(lián)不大,但是需要傳遞信息的時(shí)候傳遞,這樣就可以通過(guò)通知來(lái)實(shí)現(xiàn).
我們新建一個(gè)控制器來(lái)一鍵切換所有控制器的背景顏色.我們?yōu)槠淙∶麨?SettingViewController
創(chuàng)建一個(gè)UIButton來(lái)控制背景顏色的切換.并且為其設(shè)置觸發(fā)事件
在其出發(fā)事件中我們發(fā)送消息,并且設(shè)置通知的name為"change"用來(lái)標(biāo)記通知,設(shè)置userInfo,userInfo是一個(gè)字典,我們需要將所有控制器的顏色設(shè)置為灰色,于是,我們?cè)趧?chuàng)建字典的時(shí)候其Value值為[UIColor lioghtGrayConlor].
- (void)changeColor:(UIButton *)sender {
// 發(fā)送消息
[[NSNotificationCenter defaultCenter] postNotificationName:@"change" object:nil userInfo:@{@"color":[UIColor lightGrayColor]}];
}
這樣當(dāng)我們點(diǎn)擊該button的時(shí)候,就發(fā)送了一條消息.在其他控制器里,我們?cè)O(shè)置觀察者,注冊(cè)消息.
// 注冊(cè)消息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeTheme:) name:@"change" object:nil];
當(dāng)收到消息之后,就會(huì)執(zhí)行changeTheme:方法,我們就可在該方法中獲取發(fā)送的消息中所傳遞過(guò)來(lái)的數(shù)據(jù)(顏色信息),進(jìn)行控制器背景顏色的改變.
- (void)changeTheme:(NSNotification *)sender{
self.view.backgroundColor = sender.userInfo[@"color"];
}
在所有控制器里都添加觀察者注冊(cè)消息,并且實(shí)現(xiàn)changeTheme方法就實(shí)現(xiàn)了背景顏色的切換,而sender.userInfo[@"color"]就是從其他控制器中傳遞過(guò)來(lái)的值.我們可以改變userInfo這個(gè)字典的信息,從而實(shí)現(xiàn)其他類型的值的傳遞.
總結(jié)
以上介紹了五種界面通信的方法,由于筆者自身水平有限,其中難免會(huì)有疏漏,還請(qǐng)讀者指正,如果有不明白的問(wèn)題,歡迎在評(píng)論區(qū)留言討論指教,我將不勝感激,另,轉(zhuǎn)載請(qǐng)注明出處......