正文
在说页面传值之前先记录一下比较有意思的跳转:
业务场景:现有一个页面跳转顺序为:A-B-C-D,今需要在C push到D之后,把Cpop出来,达到页面顺序为:A-B-D。
可能有看官会说,这很简单啊:在C push到D之前,先在C里面pop一次就可以了啊,你尽管去试,能到达效果算我输!
这里有两种方案:
**第一种:**重写D的pop方法,指定他pop到特定页面也就是D,但是这里会有一个问题,如果有多个页面都能push到D,你这里指定pop到特定页面就会出问题,比如上面是A-B-D,指定D pop到B,如果有页面E-F-D的跳转,这里D pop显然是要回到F,而不是指定的B,也有人会说,那pop就分类讨论,分别pop到指定页面,但是这种方案显然是比较麻烦的,有兴趣的可以试一试。
**第二种:**直接操作导航栈,在需要跳转的位置,先把C从导航栈移除,然后再把D放入,完美的解决了我们的问题。
//C.m
D *result = [[D alloc] init];
NSMutableArray *vcs = self.navigationController.viewControllers.mutableCopy;
[vcs removeLastObject];
[vcs addObject:result];
[self.navigationController setViewControllers:vcs animated:YES];
进入正题:
页面传值是很常用的一个东西,这里介绍比较常用的五种:属性传值,block传值,代理传值,单例传值,通知传值。
(一)属性传值
**实践方案:**第二个界面中的lable显示第一个界面textField中输入的文本
实践步骤:
首先我们建立一个RootViewControllers和一个DetailViewControllers(detail页面的label显示root页面textField输入的内容),在DetailViewControllers中声明一个textString属性,用于接收传过来的字符串:
//DetailViewControllerOne.h
@interface DetailViewControllerOne : UIViewController
@property (nonatomic , strong) NSString *textString;
@end
同时创建一个Lable用来显示传过的字符串
//DetailViewControllerOne.m
@interface DetailViewControllerOne ()
@end
@implementation DetailViewControllerOne
- (void)viewDidLoad {
[super viewDidLoad];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, CGRectGetWidth(self.view.bounds)-40, 30)];
label.backgroundColor = [UIColor orangeColor];
label.font = [UIFont systemFontOfSize:20];
label.numberOfLines = 0;
label.text = self.textString; //使用传递过来的值
[self.view addSubview:label];
self.view.backgroundColor = [UIColor greenColor];
}
在RootViewControllers上引入DetailViewControllers同时声明一个textField属性用来输入字符串
//RootViewControllers.m
@interface RootViewControllerOne ()
@property(nonatomic , strong) UITextField *textField;
@end
@implementation RootViewControllerOne
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"属性传值";
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.textField];
// 创建一个轻拍手势,当点击屏幕任何一个地方,就取消键盘的第一响应,隐藏键盘
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[self.view addGestureRecognizer:tap];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(20, 200, CGRectGetWidth(self.view.bounds)-40, 40);
[button setTitle:@"下一页" forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:20];
button.backgroundColor = [UIColor greenColor];
[button addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
-(UITextField *)textField {
if (!_textField) {
_textField = [[UITextField alloc] initWithFrame:CGRectMake(20, 100, CGRectGetWidth(self.view.bounds)-40, 40)];
_textField.backgroundColor = [UIColor greenColor];
_textField.placeholder = @"请输入内容";
}
return _textField;
}
//放弃作为第一响应者
- (void)handleTap:(id)sender {
[_textField resignFirstResponder];
}
//页面跳转
-(void)clickAction:(id)sender {
DetailViewControllerOne *dVC = [[DetailViewControllerOne alloc] init];
dVC.textString = self.textField.text; //利用detail的textString属性保存textField输入的内容
[self.navigationController pushViewController:dVC animated:NO];
}
**小结:**属性传值的核心就是在一个页面通过使用另一个页面的属性,利用这个属性来保存需要传递的信息,从而达到在另一个页面能使用前一个页面传递过来的信息。
(二)Block传值
**实践方案:**当第二个页面返回第一个页面时,在第一个页面中的lable显示第二个界面textField中输入的文本
实践步骤:
首先我们建立一个RootViewControllers和一个DetailViewControllers(root页面的label显示detail页面textField输入的内容),在RootViewControllers里面新建一个用于显示的Label
//RootViewControllers.h
@interface RootViewControllerTwo : UIViewController
@property (nonatomic,retain) UILabel *label;
@end
在DetailViewControllers里面新建一个用于传值的Block,一个Block方法和一个用于输入内容的textField
//DetailViewControllers.h
typedef void (^ReturnTextBlock)(NSString *showText);//重新定义了一个名字
@interface DetailViewControllerTwo :UIViewController
@property (nonatomic,retain) UITextField *tf;
@property (nonatomic,copy) ReturnTextBlock returnTextBlock;//定义的一个Block属性
- (void)returnText:(ReturnTextBlock)block;
@end
将传递过来的block赋值给自己的属性block,然后找一个时机给block传递数据
//DetailViewControllers.m
@implementation DetailViewControllerTwo
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
//定义一个输入框 将文字传给第一个界面,并且显示在前一个页面的UILabel上
self.tf = [[UITextField alloc]initWithFrame:CGRectMake(20,100,CGRectGetWidth(self.view.bounds) - 40 , 40)];
self.tf.tintColor = [UIColor orangeColor];
self.tf.backgroundColor = [UIColor greenColor];
self.tf.placeholder = @"请输入内容";
[self.view addSubview:self.tf];
//定义一个按钮
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(20,300,CGRectGetWidth(self.view.bounds) - 40 , 40);
button.backgroundColor = [UIColor redColor];
[button setTitle:@"返回" forState:UIControlStateNormal];
[button addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
/*在第一个界面传进来一个Block语句块的函数,把传进来的Block语句块保存到本类的实例变
量returnTextBlock(.h中定义的属性)中,然后寻找一个时机调用*/
-(void)returnText:(ReturnTextBlock)block{
self.returnTextBlock = block;
}
//而这个时机就是当视图将要消失的时候,需要重写:
-(void)viewWillDisappear:(BOOL)animated{
if (self.returnTextBlock !=nil) {
self.returnTextBlock(self.tf.text);
}
}
//此处的点击事件也会触发视图消失,所以同样会走上面的viewWillDisappear方法
-(void)clickAction:(id)sender {
[self.navigationController popViewControllerAnimated:NO];
}
@end
读取block传递过来的数据,并显示在label中
//RootViewControllers.m
@interface RootViewControllerTwo ()
@end
@implementation RootViewControllerTwo
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"Block传值";
self.view.backgroundColor = [UIColor whiteColor];
//定义一个按钮
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(20,300,CGRectGetWidth(self.view.bounds) - 40 , 40);
button.backgroundColor = [UIColor blueColor];
[button setTitle:@"下一页" forState:UIControlStateNormal];
[button addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
//定义一个显示控件
self.label = [[UILabel alloc] initWithFrame:CGRectMake(20,100, CGRectGetWidth(self.view.bounds) - 40 , 40)];
self.label.backgroundColor = [UIColor purpleColor];
self.label.text = @"用于显示从后面页面返回的数据";//为了显示第二个视图控制器传过来的字符串
self.label.textColor = [UIColor whiteColor];
[self.view addSubview:self.label];
}
-(void)clickAction:(id)sender{
DetailViewControllerTwo * dVC =[[DetailViewControllerTwo alloc] init];//相对应的将其实例化,否则找不到相应的属性
//回调方法将输入框中的数据传输过来
[dVC returnText:^(NSString *showText) {
self.label.text = showText;
}];
[self.navigationController pushViewController:dVC animated:YES];
}
**小结:**其实block传值还是有点类似于属性传值,但是他是将值保存在代码块中,通过关联传递过来的代码块(页面一)与自己的属性代码块(页面二),以及使用代码块传值(页面二),回到页面一中,页面一回调代码块,以获取代码块传递过来的值。
(三)代理传值
**实践方案:**第一个界面中的lable显示第二个界面textField中输入的文本
实践步骤:
首先我们建立一个RootViewControllers和一个DetailViewControllers(root页面的label显示detail页面textField输入的内容),首先我们先声明一个代理以及代理需要实现的方法
//DetailViewController.h
@class DetailViewControllerThree;
@protocol PassingValueDeletegate <NSObject>
@optional
-(void)viewController:(DetailViewControllerThree *)viewController didPassingValueWithInfo:(id)info;
@end
@interface DetailViewControllerThree : UIViewController
@property(nonatomic, assign) id<PassingValueDeletegate> delegate;//通过代理传值
@end
在一个需要传值的时机,将需要传递的值保存到代理方法的参数中
//DetailViewController.m
@interface DetailViewControllerThree ()
@property (nonatomic, strong) UITextField *textField;
@end
@implementation DetailViewControllerThree
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(20, 300, CGRectGetWidth(self.view.bounds)-40, 40);
[button setTitle:@"返回" forState:UIControlStateNormal];
button.backgroundColor = [UIColor blueColor];
[button addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
[self.view addSubview:self.textField];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSString *string;
if ([_textField.text length] == 0) {
string = @"用户未输入任何内容";
}else {
string = _textField.text;
}
//视图将要消失,通过代理传值
//首次判断代理是否存在,并在代理能够响应代理方法时才执行代理方法
if (self.delegate && [self.delegate respondsToSelector:@selector(viewController:didPassingValueWithInfo:)]) {
[self.delegate viewController:self didPassingValueWithInfo:string];
}
}
-(UITextField *)textField {
if (!_textField) {
_textField = [[UITextField alloc] initWithFrame:CGRectMake(20, 100, CGRectGetWidth(self.view.bounds) - 40, 40)];
}
_textField.placeholder = @"请输入内容";
_textField.backgroundColor = [UIColor greenColor];
return _textField;
}
-(void)clickAction:(id)sender {
[self.navigationController popViewControllerAnimated:NO];
}
声明RootViewController实现该代理,并实现该代理的方法,而该代理方法就包含着传递过来的值