专栏名称: Adrenine
iOS开发
51好读  ›  专栏  ›  Adrenine

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

Adrenine  · 掘金  ·  · 2017-12-13 08:46

正文

在说页面传值之前先记录一下比较有意思的跳转: 业务场景:现有一个页面跳转顺序为: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实现该代理,并实现该代理的方法,而该代理方法就包含着传递过来的值







请到「今天看啥」查看全文