浅谈iOS 开发中的界面通信

在任何的软件开发中都离不开界面与界面之间的通信,界面通信的最直接的方法就是界面传值.

在开发过程中我们在页面传值时我们通常使用的方法有:属性传值法,block传值法,代理传值法,以及单例传值法,通知传值法

属性传值

属性传值多用于在将前一个页面的值传到后一个页面去,也就是我们通常说的从前往后传值

当第一个页面push到第二个页面的时候,我们在第二个页面声明一个属性用于接受从第一个页面传递过去的值,然后在push这个事件被触发的时候进行赋值,也就是说,先初始化创建第二个控制器(页面),然后通过创建的控制器来访问它所对应的属性,将即将传递的值赋给它,这样就完成了属性传值.于是当页面(控制器)被push到第二个页面之后我们访问它的属性的时候,也就顺便获取到了传递过来的值.

例如:现在有两个控制器FirstViewController和SecondViewController.我们在FirstViewController中创建一个UITextFiled,在SecondViewController中创建一个UILabel,然后在textField中输入文字,我们使用导航控制器来控制页面之间的跳转,当我们跳转到第二个页面的时候,将在textField中输入的文字传递给label,作为label上的文字.

代码如下:

FirstViewController中:

self.navigationItem.title = @"First";
// 设置控制器的背景颜色
self.view.backgroundColor = [UIColor yellowColor];
// 设置控制器的右按钮,并创建事件点击调到下一页面
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"第二页" style:UIBarButtonItemStyleDone target:self action:@selector(next:)];
// 创建输入框
self.firstTF = [[UITextField alloc] initWithFrame:CGRectMake(50, 100, 314, 40)];
self.firstTF.placeholder = @"请输入文字!";
_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];
// 由上一个页面通过属性传值传过来
self.secondLabel.text = _tfString;
[self.view addSubview:_secondLabel];

我们在SecondViewController的.h文件中设置一个字符串属性用来接收从FirstViewController中传递过来的值.

@property(nonatomic,strong)NSString *tfString;

那么我们在什么时候传递这个值呢?

合适的地方应该是在第一个页面跳转到第二个页面的时候创建,也就是在push方法执行之前进行传值.我们在FirsrViewController的右按钮点击事件也就是next方法中进行传值.

-(void)next:(UINavigationController *)sender{
    ScondViewController *secondNC = [[ScondViewController alloc] init];
    // 将第一个界面的输入框信息赋值给第二个页面的Label
    secondNC.tfString = _firstTF.text;
    [self.navigationController pushViewController:secondNC animated:YES];   
}

这样就实现了从前往后传值,也就是属性传值

代理传值

代理传值多用于从后往前传值.

我们还是使用上面的额例子,比如我们现在在SecondViewController中进行的相关的操作,现在我们要返回到第一个页面,但是我们现在有一个需求就是需要在SecondViewController中创建一个UITextField,在FirstViewController中创建一个UILabel,当页面pop回第一个页面的时候将SecondViewController中textField中的文字传递到FirstViewController中的label上,这个时候,我们就可以使用代理传值.

我们在SecondViewController的.h中创建一个协议,并且设置代理属性,让其遵循该协议.

代码如下:

// 设置一个协议方法
@protocol SecondVCDelegate 
    // 代理传值 
    - (void)passValue:(NSString *)value;
@end
// 声明代理属性进行代理传值
@property(nonatomic,weak)id delegate;

在SecondViewController中创建textField

    self.secondTF = [[UITextField alloc] initWithFrame:CGRectMake(50, 200, 314, 40)];
    self.secondTF.placeholder = @"请输入";
    self.secondTF.borderStyle = UITextBorderStyleLine;
    [self.view addSubview:_secondTF];

在FirstViewController中创建label并让其遵循协议

#import "FirstViewController.h"
#import "ScondViewController.h"
@interface FirstViewController ()

@property(nonatomic,strong)UILabel *firstLab;

@end

创建UILabel

    self.firstLab = [[UILabel alloc] initWithFrame:CGRectMake(50, 200, 314, 40)];
    self.firstLab.backgroundColor = [UIColor magentaColor];
    [self.view addSubview:_firstLab];

我们在上述属性传值代码中已经为SecondViewController的导航控制器设置了右按钮(返回按钮),那么,我们只需要在其触发事件(back:)中让代理去执行传值即可.
代码如下:

- (void)back:(UINavigationController *)sender{
    // 代理去执行传值
    [_delegate passValue:_secondTF.text];
    [self.navigationController popViewControllerAnimated:YES]; 
}

然后在FirstViewController的next方法中为SecondNC制定其代理为其自身即可.

实现其代理方法

- (void)passValue:(NSString *)value{
    _firstLab.text = value; 
}

这样我们就可以在第一个页面得到第二个页面的值了.

block传值

block的本质就和其他OC中变量类似,只不过,block中存储的数据是函数体,但是在使用block时完全可以像调用其他函数似的,传入参数,然后得到返回值.关于详细的block,在这里就不赘述了.

在iOS开发中我们用到block进行传值的情况多数情况下也是在从后往前传值.所以我们依然使用上述例子.

使用block传值,首先我们需要在SecondViewController中定义并声明block属性.

// 定义有参无返回值的匿名函数(传递字符串)
typedef void(^MyBlock)(NSString *);
@interface SecondViewController : UIViewController
@property(nonatomic,copy)MyBlock block;
@end

同上,我们将SecondViewController中textField中输入的文字传递到FirstViewController中的label上显示.

我们在SecondViewController的back方法中调用block,并且将在这个控制器的textField中的文字作为block的参数传递给block.

- (void)back:(UINavigationController *)sender{
    // 代理去执行传值
    self.block(_secondTF.text);
    [self.navigationController popViewControllerAnimated:YES]; 
}

然后我们在FirstViewController的next方法中,也就是alloc出SecondViewController的时候调用SecondViewController的block,实现传值

- (void)back:(UINavigationController *)sender{
    __weak typeof(self)temp = self;
    secVC.block = ^(NSString *string){
        // 通过回调将传进来的字符串赋值给label
        temp.firstLab.text = string;
    };
    [self.navigationController popViewControllerAnimated:YES]; 
}

在上述代码中,因为block里面不能直接使用属性,实例变量和方法(因为会造成循环引用),所以我们重新用__weak修饰self并重新命名为temp.这样我们就实现了传值.

单例传值

由于单例在内存中只创建一次的并且可以全局访问的属性,我们可以在必要的时候将数据存放在单例的属性中,并且在必要的时候从单例中通过访问其属性进行调用,这样就实现值的传递

首先我们先创建一个单例类DataHandle,在其.h文件中我们使用类方法声明单例创建的方法,并且声明一个字符串属性用来保存值,(因为我们依然使用上述的例子,所以声明字符串,来保存textField中输入的文字,在实际的开发过程中,读者可以自己根据实际情况而定).

@interface DataHandle : NSObject
// 创建单例
+ (instancetype)sharedDataHandle;
@property(nonatomic,strong)NSString *Str;
@end

在.m文件中实现其初始化方法

@implementation DataHandle
// 声明静态区对象的原因,希望程序运行期间,在内存中一直存在,这样对外界来说,可以随时读取数据
static DataHandle *dataHandle = nil;
// 创建单例(全局区)
+ (instancetype)sharedDataHandle{
    if (nil == dataHandle) {
        // 我们创建单例使用加号方法的原因是因为,在创建之前,无法存在一个实例对象去调用动态方法来创建它本身
        dataHandle = [[DataHandle alloc] init];
    }
    return dataHandle; 
}
@end

无论是从FirstViewController中将textField的值传到SecondViewController的label上还是反过来传值.我们只需要在需要的时候通过其类方法(+ (instancetype)sharedDataHandle)来创建出单例对象来,然后将textField的text属性以赋值的方式赋给单例的Srt属性即可

代码如下:

-(void)next:(UINavigationController *)sender{
    DataHandle *dataHandle = [DataHandle sharedDataHandle];
    ScondViewController *secondNC = [[ScondViewController alloc] init];
    // 将第一个界面的输入框信息赋值给第二个页面的Label
    dataHandle.Str = _firstTF.text;
    [self.navigationController pushViewController:secondNC animated:YES];   
}

假使在SecondViewController中我们为label的text赋值,而其值就是FirstViewController中在textField中输入的值,那么我们可以这样写:

DataHandle *dataHandle = [DataHandle sharedDataHandle];
self.secondLabel.text = dataHandle.str;

如上所述就实现了值的传递.
其实单例不知是在两个页面之间进行传递,由于单例的一次创建全局访问的特点,我们可以将值传递到ThreeViewController,FourViewController等等的控制器,相反的也可以反向传递,在此就不在重复了,其实原理都是相同的.

通知传值

通知在iOS开发中占据着非常重要的地位,通常在两个页面关联不大,但是需要传递信息的时候传递,这样就可以通过通知来实现.

我们新建一个控制器来一键切换所有控制器的背景颜色.我们为其取名为:SettingViewController

创建一个UIButton来控制背景颜色的切换.并且为其设置触发事件
在其出发事件中我们发送消息,并且设置通知的name为"change"用来标记通知,设置userInfo,userInfo是一个字典,我们需要将所有控制器的颜色设置为灰色,于是,我们在创建字典的时候其Value值为[UIColor lioghtGrayConlor].

- (void)changeColor:(UIButton *)sender {   
    // 发送消息
    [[NSNotificationCenter defaultCenter] postNotificationName:@"change" object:nil userInfo:@{@"color":[UIColor lightGrayColor]}];
}

这样当我们点击该button的时候,就发送了一条消息.在其他控制器里,我们设置观察者,注册消息.

// 注册消息
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeTheme:) name:@"change" object:nil];

当收到消息之后,就会执行changeTheme:方法,我们就可在该方法中获取发送的消息中所传递过来的数据(颜色信息),进行控制器背景颜色的改变.

- (void)changeTheme:(NSNotification *)sender{ 
    self.view.backgroundColor = sender.userInfo[@"color"];  
}

在所有控制器里都添加观察者注册消息,并且实现changeTheme方法就实现了背景颜色的切换,而sender.userInfo[@"color"]就是从其他控制器中传递过来的值.我们可以改变userInfo这个字典的信息,从而实现其他类型的值的传递.

总结

以上介绍了五种界面通信的方法,由于笔者自身水平有限,其中难免会有疏漏,还请读者指正,如果有不明白的问题,欢迎在评论区留言讨论指教,我将不胜感激,另,转载请注明出处......

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 在iOS开发过程中,界面间数据传输是最为基本的需求,苹果公司为我们开发中提供了多种传值方式,今天我们来谈一谈较为常...
    呼噜ZR阅读 7,160评论 2 38
  • 在我们需要在另一个页面中获取到这个页面个的数据的时候,我们就需要将这个页面通过某种形式传递给另一个页面。在这里我们...
    Joker_King阅读 5,491评论 5 16
  • 在进行页面跳转时,往往都需要携带数据,以便于对界面进行数据的初始化,方便用户查看一些信息、状态等,以下是本人在开发...
    笑望書阅读 1,403评论 0 2
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,600评论 18 139
  • 不知道为什么,晚上到可坊颂喝奶茶看杂志的时候很巧的碰上了上回同事介绍的相亲对象。同样的下雨天,同样有格调的背景音乐...
    滴答滴答滴阅读 1,118评论 0 1