目录
一、属性传值
属性传值适用于从前往后传。如果要从A往B传一个值,就首先在B界面的接口部分定义一个属性,然后在A界面创建一个B界面的实例对象,并给B的实例对象赋值并跳转视图控制器,这样我们赋的值就被传递给了B页面。
A界面实现部分:
- (void)pressGoB {
B_ViewController *bView = [[B_ViewController alloc] init];
bView.aToBStr = @"从A传来的值";
[self.navigationController pushViewController:bView animated:YES];
}
B界面接口部分:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface B_ViewController : UIViewController
@property (nonatomic, copy) NSString *aToBStr;
@end
NS_ASSUME_NONNULL_END
B界面实现部分:
#import "B_ViewController.h"
@interface B_ViewController ()
@end
@implementation B_ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
UILabel *BLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 300, 200, 50)];
BLabel.text = self.aToBStr;
[self.view addSubview:BLabel];
}
@end
结果:
二、协议传值
协议传值适用于从后往前传值。假设要从B视图往A视图传值,我们首先需要在B视图的接口部分定义一个协议并且为该协议添加一个方法,该方法的参数就是我们要传的值,然后添加代理者属性用于执行协议。然后在从B视图跳转到A视图的地方设定好代理 delegate的对象执行协议方法,最后在接收值的A界面遵循协议并且在A跳转到B的地方将B的代理者属性设定为A视图控制器,最后实现相应的协议方法即可。
B接口:定义协议、添加方法、创建代理者属性:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
//创建协议
@protocol BToADelegate <NSObject>
//添加方法
- (void)BToAMode: (NSString*) bToAStr;
@end
@interface B_ViewController : UIViewController
@property (nonatomic, copy) NSString *aToBStr;
//设置代理者属性
@property (nonatomic, assign) id<BToADelegate> delegate;
@end
NS_ASSUME_NONNULL_END
B实现:设定好代理 delegate的对象执行协议方法
- (void)pressGoA {
//设定好代理 delegate的对象执行协议方法
[self.delegate BToAMode:@"从B传来的值(协议)"];
[self.navigationController popViewControllerAnimated:YES];
}
A接口:遵守协议
#import <UIKit/UIKit.h>
#import "B_ViewController.h"
NS_ASSUME_NONNULL_BEGIN
//遵守代理
@interface A_ViewController : UIViewController<BToADelegate>
@property (nonatomic, strong) UILabel *ALabel;
@end
NS_ASSUME_NONNULL_END
A实现:将B对象的代理者属性设置为A,并且实现相应的协议方法
//A跳转到B视图的函数
- (void)pressGoB {
B_ViewController *bView = [[B_ViewController alloc] init];
bView.aToBStr = @"从A传来的值";
//将B对象的代理者属性设置为A
bView.delegate = self;
[self.navigationController pushViewController:bView animated:YES];
}
//协议方法,传来的bToAStr就是传来的值
- (void)BToAMode:(NSString *)bToAStr {
self.ALabel.text = bToAStr;
}
结果:
三、Block传值
Block传值也是一种适用于从后往前传的传值方式,该方式的优点是相较于协议传值,它更简便。它传入的Block块本质上和其他变量一样,不过不同的是它存储的是一个函数体,我们可以直接在这个函数体中进行相应的赋值等操作。如果现在要从B向A传值,我们首先应该在B的接口部分声明一个Block属性,然后在由B跳转回A的位置将需要传递的值放入该属性的参数部分,最后在A跳转到B的位置调用B页面的Block属性,并在Block块中完成相应的操作。
B接口:声明Block属性:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
//声明一个Block属性,传入两个参数
//第一个参数指Block块的返回类型,第二个参数指该Blcok块与其参数
typedef void (^BToABlock)(NSString *bToAStr);
@interface B_ViewController : UIViewController
@property (nonatomic, copy) NSString *aToBStr;
//声明属性
@property (nonatomic, strong) BToABlock bToABlock;
@end
NS_ASSUME_NONNULL_END
B实现:将需要传递的值放入该属性的参数部分:
- (void)pressGoA {
//放入要传递的值
self.bToABlock(@"从B传来的值(Block)");
[self.navigationController popViewControllerAnimated:YES];
}
A实现:调用B对象的Block属性
- (void)pressGoB {
B_ViewController *bView = [[B_ViewController alloc] init];
bView.aToBStr = @"从A传来的值";
//调用B对象的Block属性
bView.bToABlock = ^(NSString *bToAStr) {
self.ALabel.text = bToAStr;
};
[self.navigationController pushViewController:bView animated:YES];
}
结果:
四、通知传值
通知传值即可以从前往后传,也可以从后往前传值,并且该方法可以实现跨界面传值。使用通知传值一共有四个步骤,假设要在没有直接跳转关系的A和C两个界面间传值,若从C向A传值,首先需要在C界面实现一个方法发送通知,然后在A界面注册通知,也就是接收通知,最后在A界面实现通知中的方法即可。在不需要通知的地方要移除通知。
C实现:实现通知方法:
@interface C_ViewController ()
@end
@implementation C_ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
NSDictionary *CToADictionary = [NSDictionary dictionaryWithObject:@"从C传来的值(通知)" forKey:@"label"];
//通知方法
[[NSNotificationCenter defaultCenter] postNotificationName:@"CToAString" object:nil userInfo:CToADictionary];
//传入三个参数
//第一个postNotificationName:该通知的名字,通知者和接收者的名字应该一样才能完成接收
//第二个object:接收对象
//第三个userInfo:通知携带的参数,该参数必须是一个字典对象,因此我们在传值之前应该定义一个字典并添加入我们要传的值
UIButton *goAbutton = [UIButton buttonWithType:UIButtonTypeSystem];
[goAbutton setTitle:@"返回到A界面" forState:UIControlStateNormal];
goAbutton.frame = CGRectMake(100, 450, 100, 50);
[goAbutton addTarget:self action:@selector(pressGoA) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:goAbutton];
}
- (void)pressGoA {
[self.navigationController popToRootViewControllerAnimated:YES];
}
A实现:注册通知,并实现通知中的方法
#import "A_ViewController.h"
#import "B_ViewController.h"
@interface A_ViewController ()
@end
@implementation A_ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.ALabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 300, 200, 50)];
[self.view addSubview:self.ALabel];
UIButton *goBbutton = [UIButton buttonWithType:UIButtonTypeSystem];
[goBbutton setTitle:@"跳转到B界面" forState:UIControlStateNormal];
goBbutton.frame = CGRectMake(100, 450, 100, 50);
[goBbutton addTarget:self action:@selector(pressGoB) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:goBbutton];
//注册通知,该方法传入的name必须与通知者的name相同才能完成通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(CToAPress:) name:@"CToAString" object:nil];
//CtoAPress:是我们要实现的方法
/*addObserver:selector:name:object: 是 NSNotificationCenter 类的方法,用于向通知中心注册观察者。
该方法接受四个参数:
1. observer:要注册的观察者对象,通常是调用该方法的实例对象。
2. selector:触发通知时要调用的方法,该方法在观察者对象中定义。它接受一个 NSNotification 对象作为唯一参数。
3. name:通知的名称,一个字符串,可以是自定义的或预定义的通知名称。
4. object:发送通知的对象。如果指定为 nil,则接收所有对象发送的通知;如果指定为特定对象,则只接收该对象发送的通知。*/
}
//该方法的参数是通知对象,在该方法中,我们要取值则需要调用此通知对象的userInfo属性,该属性就是我们传来的字典
- (void) CToAPress: (NSNotification*) sender {
self.ALabel.text = sender.userInfo[@"label"];
}
在不用的时候要移除通知:
- (void)dealloc {
//移除所有通知的方法:
//[[NSNotificationCenter defaultCenter] removeObserver:self];
//移除某个通知的方法:
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"TransDataNoti" object:nil];
}
结果:
五、KVO传值
KVO全称Key Value Observing。KVO传值允许对象监听另一个对象的特定属性,当该属性改变的时候,会触发事件。
KVO不仅可以监听单个属性的变化,也可以监听集合对象的变化。监听集合对象的时候,通过KVC
的mutableArrayValueForKey:
等方法获得代理对象,当代理对象的内部对象发生改变时,会回调KVO
监听的方法。集合对象包含NSArray
和NSSet
。
这里我们用一个案例来说明KVO传值:首先我们在A页面布局一个视图,再创建一个B类,该类只有一个age属性,然后再A页面通过KVO监听B类的age属性,并设置按钮改变B的age属性的值来触发监听方法,在监听方法中,对页面的label进行赋值。
A页面:
#import <UIKit/UIKit.h>
#import "AViewController.h"
@interface ViewController : UIViewController
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, strong) UIButton *button;
@property (nonatomic, strong) AViewController *aView;
@end
#import "ViewController.h"
@interface ViewController ()
@end
static int a = 0;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.label = [[UILabel alloc] init];
self.label.frame = CGRectMake(100, 200, 200, 80);
[self.view addSubview:self.label];
self.button = [UIButton buttonWithType:UIButtonTypeSystem];
self.button.frame = CGRectMake(100, 400, 100, 80);
[self.button setTitle:@"+1" forState:UIControlStateNormal];
[self.button addTarget:self action:@selector(pressAdd) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.button];
self.aView = [[AViewController alloc] init];
//注册监听者
//@observer:就是观察者,是谁想要观测对象的值的改变。
//@keyPath:就是想要观察的对象属性。
//@options:@options分为四个类型:
//NSKeyValueObservingOptionNew 返回改变后的新值;NSKeyValueObservingOptionOld 返回改变之前的旧值;NSKeyValueObservingOptionInitial 注册的时候就会发一次通知,改变后也会发通知;NSKeyValueObservingOptionPrior 改变之前发一次通知,改变之后发一次通知
//@context:想要携带的其他信息,比如一个字符串或者字典什么的,不过只能是同一视图的内容。
[self.aView addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}
//实现UIButton的触发事件,用来触发监听
- (void)pressAdd {
//改变aView中的age的值会调用监听方法
self.aView.age = a++;
NSLog(@"%d",self.aView.age);
}
//监听方法,当KVO事件到来的时候会调用这个方法
//@keyPath:观察的属性
//@object:观察的是哪个对象的属性
//@change:这是一个字典类型的值,通过键值对显示新的属性值和旧的属性值,新值的key是@"new",旧值的key是@"old"
//@context:上面添加观察者时携带的信息
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
//通过调用监听事件,来改变label显示的内容
self.label.text = [NSString stringWithFormat:@"显示的数字是: %d",self.aView.age];
//输出改变前的旧值和改变后的新值
NSLog(@"旧值:%@,新值:%@",[change valueForKey:@"old"], [change valueForKey:@"new"]);
}
//在不使用的时候移除监听
- (void)dealloc {
[self.aView removeObserver:self forKeyPath:@"age"];
}
@end
B类属性:
@interface AViewController : UIViewController
@property (nonatomic, assign) int age;
@end
结果:
点击+1后:
注:
- KVO只是监听setter方法,例如像可变数组添加元素的方法(addObject)它不属于setter方法,所以即使你向数组中add多少个元素也不会有监听反应。
- 在不使用时一定要移除KVO。