在说页面传值之前先记录一下比较有意思的跳转:
业务场景:现有一个页面跳转顺序为: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
#import <UIKit/UIKit.h>
@interface DetailViewControllerOne : UIViewController
@property (nonatomic , strong) NSString *textString;
@end
同时创建一个Lable用来显示传过的字符串
//DetailViewControllerOne.m
#import "DetailViewControllerOne.h"
@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
#import "RootViewControllerOne.h"
#import "DetailViewControllerOne.h"
@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
#import <UIKit/UIKit.h>
@interface RootViewControllerTwo : UIViewController
@property (nonatomic,retain) UILabel *label;
@end
在DetailViewControllers里面新建一个用于传值的Block,一个Block方法和一个用于输入内容的textField
//DetailViewControllers.h
#import <UIKit/UIKit.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
#import "DetailViewControllerTwo.h"
#import "RootViewControllerTwo.h"
@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
#import "RootViewControllerTwo.h"
#import "DetailViewControllerTwo.h"
@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
#import <UIKit/UIKit.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
#import "DetailViewControllerThree.h"
@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实现该代理,并实现该代理的方法,而该代理方法就包含着传递过来的值
//RootViewController.m
#import "RootViewControllerThree.h"
#import "DetailViewControllerThree.h"
@interface RootViewControllerThree () <PassingValueDeletegate>
@property (nonatomic, strong) UILabel *showLabel;
@end
@implementation RootViewControllerThree
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"代理传值";
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.showLabel];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(20, 300, 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];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (UILabel *)showLabel {
if (!_showLabel) {
_showLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 200, CGRectGetWidth(self.view.bounds)-40, 40)];
}
_showLabel.text = @"用于显示后面页面传过来的值";
_showLabel.textColor = [UIColor whiteColor];
_showLabel.backgroundColor = [UIColor purpleColor];
return _showLabel;
}
-(void)clickAction:(id)sender {
DetailViewControllerThree *dVC = [[DetailViewControllerThree alloc] init];
dVC.delegate = self;
[self.navigationController pushViewController:dVC animated:NO];
}
-(void)viewController:(DetailViewControllerThree *)viewController didPassingValueWithInfo:(id)info {
_showLabel.text = info; //代理方法传递过来的值
}
小结:代理方法是用的比较多的,适用于任意界面之间传值,只需要声明实现代理方法,就可以获取传递过来的值
(四)单例传值
实践方案:第一个界面中的lable显示第二个界面textField中输入的文本,同时第二个界面中的lable显示第一个界面textField中输入的文本,输入文本互相传递
实践步骤:新建一个单例
#import <Foundation/Foundation.h>
@interface AppStatus : NSObject {
NSString *_contextStr;
}
@property(nonatomic,retain)NSString *contextStr;
+(AppStatus *)shareInstance;
@end
#import "AppStatus.h"
@implementation AppStatus
@synthesize contextStr = _contextStr;
static AppStatus *_instance = nil;
+(AppStatus *)shareInstance
{
if (_instance == nil)
{
_instance = [[super alloc]init];
}
return _instance;
}
-(id)init
{
if (self = [super init])
{
}
return self;
}
@end
#import "RootViewControllerFour.h"
#import "AppStatus.h"
#import "DetailViewControllerFour.h"
@interface RootViewControllerFour ()
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, strong) UITextField *textField;
@end
@implementation RootViewControllerFour
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"单例传值";
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(20, 300, CGRectGetWidth(self.view.bounds) -40, 40);
[btn setTitle:@"Push" forState:UIControlStateNormal];
btn.backgroundColor = [UIColor redColor];
[btn addTarget:self action:@selector(pushAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
[self.view addSubview:self.textField];
self.label.frame = CGRectMake(20, 100, CGRectGetWidth(self.view.bounds) -40, 40);
self.label.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.label];
}
-(void)viewWillAppear:(BOOL)animated {
if ([AppStatus shareInstance].contextStr.length !=0) {
self.label.text = [AppStatus shareInstance].contextStr;
} else {
self.label.text = @"用于现实后面页面传递过来的值";
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(UITextField *)textField {
if (!_textField) {
_textField = [[UITextField alloc] initWithFrame:CGRectMake(20, 200, CGRectGetWidth(self.view.bounds)-40, 40)];
}
_textField.placeholder = @"请输入内容";
_textField.backgroundColor = [UIColor purpleColor];
_textField.textColor = [UIColor whiteColor];
return _textField;
}
-(void)pushAction:(id)sender
{
// _textField = (UITextField *)[self.view viewWithTag:1000];
//单例传值 将要传递的信息存入单例中(共享中)
// [[AppStatus shareInstance]setContextStr:tf.text]; 跟下面这种写法是等价的
[AppStatus shareInstance].contextStr = _textField.text;
//导航push到下一个页面
//pushViewController 入栈引用计数+1,且控制权归系统
DetailViewControllerFour *detailViewController = [[DetailViewControllerFour alloc]init];
//导航push到下一个页面
[self.navigationController pushViewController:detailViewController animated:YES];
}
#pragma mark - Getter & Setter
LabelGetter(label, NSTextAlignmentCenter, ColorFromRGB(0xffffff), [UIFont systemFontOfSize:15])
#import "DetailViewControllerFour.h"
#import "AppStatus.h"
@interface DetailViewControllerFour ()
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, strong) UITextField *textField;
@end
@implementation DetailViewControllerFour
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
self.label.frame = CGRectMake(20, 100, CGRectGetWidth(self.view.bounds) -40, 40);
self.label.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.label];
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(20, 200, CGRectGetWidth(self.view.bounds) -40, 40)];
self.textField.placeholder = @"请输入内容";
self.textField.backgroundColor = [UIColor purpleColor];
self.textField.textColor = [UIColor whiteColor];
[self.view addSubview:self.textField];
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(doneAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewWillAppear:(BOOL)animated {
if ([AppStatus shareInstance].contextStr.length !=0) {
self.label.text = [AppStatus shareInstance].contextStr;
} else {
self.label.text = @"用于现实前面页面传递过来的值";
}
}
//pop回前一个页面
-(void)doneAction:(id)sender {
//单例传值
[AppStatus shareInstance].contextStr = _textField.text;
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark - Getter & Setter
LabelGetter(label, NSTextAlignmentCenter, ColorFromRGB(0xffffff), [UIFont systemFontOfSize:15])
(五)通知传值
实践方案:第一个界面中的lable显示第二个界面textField中输入的文本
实践步骤:
首先我们建立一个RootViewControllers和一个DetailViewControllers(root页面的label显示detail页面textField输入的内容),首先我们RootViewController里面注册一个通知监听,并在页面消失时移除该通知(注册与移除需要对应)
#import "RootViewControllerFive.h"
#import "DetailViewControllerFive.h"
#define xbyNotification @"labelChange"
@interface RootViewControllerFive ()
@property (nonatomic, strong) UILabel *label;
@end
@implementation RootViewControllerFive
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor greenColor];
self.title = @"通知传值";
[self.view addSubview:self.label];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(20, 300, CGRectGetWidth(self.view.bounds)-40, 40);
[button setTitle:@"点击" forState:UIControlStateNormal];
[button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[button addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor blueColor];
[self.view addSubview:button];
}
-(void)viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(labelTextChange:) name:xbyNotification object:nil];
}
//一开始准备在这移除消息通知,结果GG了,啥通知都收不到
//-(void)viewWillDisappear:(BOOL)animated {
// [super viewDidDisappear:animated];
// [[NSNotificationCenter defaultCenter] removeObserver:self];
//}
//放在这就好了
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(UILabel *)label {
if (!_label) {
_label = [[UILabel alloc ] initWithFrame:CGRectMake(20, 100, CGRectGetWidth(self.view.bounds)-40, 40)];
_label.textAlignment = NSTextAlignmentCenter;
_label.backgroundColor = [UIColor purpleColor];
_label.text = @"等待接收通知消息";
_label.textColor = [UIColor whiteColor];
}
return _label;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)clickAction:(id)sender {
[self.navigationController pushViewController:[DetailViewControllerFive new] animated:NO];
}
-(void)labelTextChange:(NSNotification *)sender {
NSDictionary *dic = sender.userInfo;
self.label.text = dic[@"info"];
// NSLog(@"收到通知");
}
点击按钮时发送通知
#import "DetailViewControllerFive.h"
#define xbyNotification @"labelChange"
@interface DetailViewControllerFive ()
@property (nonatomic, strong) UITextField *textField;
@end
@implementation DetailViewControllerFive
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view addSubview:self.textField];
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 redColor];
[button addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(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 {
[[NSNotificationCenter defaultCenter] postNotificationName:xbyNotification object:self userInfo:@{@"info":_textField.text}];
[self.navigationController popViewControllerAnimated:NO];
}
小结:注册通知与移除通知需要一一对应,同时通知名称要相同,才能收到该通知发送的消息。
Demo下载