iOS笔记之五种页面传值方式

在说页面传值之前先记录一下比较有意思的跳转:
业务场景:现有一个页面跳转顺序为: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 

@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 

@interface RootViewControllerTwo : UIViewController

@property (nonatomic,retain) UILabel *label;

@end

在DetailViewControllers里面新建一个用于传值的Block,一个Block方法和一个用于输入内容的textField

//DetailViewControllers.h

#import 

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 

@class DetailViewControllerThree;
@protocol PassingValueDeletegate 

@optional
-(void)viewController:(DetailViewControllerThree *)viewController didPassingValueWithInfo:(id)info;

@end

@interface DetailViewControllerThree : UIViewController

@property(nonatomic, assign) id 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 () 

@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 

@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下载

你可能感兴趣的:(iOS笔记之五种页面传值方式)