iOS史上最全界面跳转传值总结及分析-包括属性传值、代理传值、通知传值、单例传值、代码块传值
界面跳转传值实际上就是将数据从一个界面传送给另一个界面,本文中的传值是基于UINavigationController,首先在AppDelegate里面创建一个UINavigationController,把ViewController作为其跟视图,别忘了在AppDelegate.m导入ViewController.h。代码如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindowalloc]initWithFrame:[UIScreenmainScreen].bounds];
UINavigationController * nav = [[UINavigationControlleralloc]initWithRootViewController:[ViewControllernew]];
self.window.rootViewController = nav;
nav.navigationBar.translucent =NO;
[self.windowmakeKeyAndVisible];
return YES;
}
新建一个继承于UIViewController的视图控制器,本文命名为NextViewController并令ViewController为A,NextViewController为B。并在两个控制器分别放上一个button和一个label,label作为属性,button可以作为局部变量只要增加一个点击事件就可;
在ViewDidLoad中加入如下代码:
self.view.backgroundColor = [UIColorblueColor];
UIButton * btnProperty = [UIButtonbuttonWithType:UIButtonTypeRoundedRect];
btnProperty.frame =CGRectMake(100,60, 175, 40);
[btnProperty setTitle:@"属性传值"forState:UIControlStateNormal];
[btnProperty addTarget:selfaction:@selector(btnProperty)forControlEvents:UIControlEventTouchUpInside];
[btnProperty setBackgroundColor:[UIColorgreenColor]];
[self.viewaddSubview:btnProperty];
[self.viewaddSubview:self.lblProperty];
另外给self.lblProperty做一个懒加载
- (UILabel *)lblProperty{
if (!_lblProperty) {
_lblProperty = [[UILabelalloc]initWithFrame:CGRectMake(100,20, 175, 30)];
_lblProperty.backgroundColor = [UIColorwhiteColor];
}
return_lblProperty;
}
在NextViewController也加入相同代码,只需把界面颜色换一下区分开即可;每种传值都需要添加button个label,厦门就不在重复了;1.属性传值
属性传值即给相应的ViewController定义一个外部可用的属性,在界面跳转时来传送相应的值,所有传值都可分为A-->B和B-->A,其中A为跟视图,B为新视图。
(1)属性传值A-->B
通过属性由A传值到B不难理解,只需要在B界面定义一个属性,在A PUSH到B的时候把需要传送的内容赋值给B对应的属性即可,代码如下:
① 在NextViewController.h 定义一个属性(本文以字符串为例):
@property (nonatomic,strong) NSString * strProperty2;
//属性传值
- (void)btnProperty{
NextViewController * nextVC = [[NextViewControlleralloc]init];
nextVC.strProperty2 =@"厦门博看文思";
[self.navigationControllerpushViewController:nextVC animated:YES];
}
③这时B控制器已经拿到了A传来的值,只需要在viewDidLoad做个赋值操作即可展现出传过来的字符串了:
//属性传值
self.lblProperty.text =self.strProperty2;
(2)属性传值B-->A
属性由B到A传值会比A到B稍微复杂一点不过也是分为三步:
①给A定义一个外部可用属性:
@property (nonatomic,strong) NSString * strProperty1;
②在B跳转回A的点击事件中找到A控制器并给其属性赋值,这里拿到A控制器就不能跟A-->B那样直接新建一个控制器了,因为A控制器还存在,只是没有显示出来而已;所以应该通过导航栏控制器中获取当前A控制器:
- (void)btnProperty{
ViewController * VC =self.navigationController.viewControllers[0];
VC.strProperty1 =@"iOS";
[self.navigationControllerpopViewControllerAnimated:YES];
}
③这时A中已经拿到了B传过来的值,但是要显示的话应该在viewWillAppear中:
- (void)viewWillAppear:(BOOL)animated{
// 属性传值
self.lblProperty.text =self.strProperty1;
}
2.代理传值
(1)代理传值A-->B
@protocol ViewControllerDelegate <NSObject>
@optional
- (void)delegateSendToNext:(NSString * )strDelegate;
@end
②并在@interface中添加遵循协议的代理属性
@property (nonatomic,assign)id<ViewControllerDelegate> delegate;
③在B的头文件中遵循A的协议;
@interface NextViewController :UIViewController<ViewControllerDelegate>
//代理传值
- (void)btnDelegate{
NextViewController * nextVC = [[NextViewControlleralloc]init];
self.delegate = nextVC;
[self.delegatedelegateSendToNext:@"厦门博看文思"];
[self.navigationControllerpushViewController:nextVC animated:YES];
}
//在B控制器实现A控制器中的代理方法
- (void)delegateSendToNext:(NSString * )strDelegate{
self.lblDelegate.text = strDelegate;
}
(2)代理B-->A
①在B控制器头文件申明协议;
@protocol NextViewControllerDelegate <NSObject>
@optional
- (void)delegateSend:(NSString *)strDelegate;
@end
②在B控制器定义遵循协议的属性;
@property (nonatomic,assign) id<NextViewControllerDelegate> delegate;
③在B跳回A的点击事件中调用代理方法;
- (void)btnDelegate{
[self.delegatedelegateSend:@"iOS"];
[self.navigationControllerpopViewControllerAnimated:YES];
}
④A遵循B的协议;
@interface ViewController ()<NextViewControllerDelegate>
⑤在A跳转到B的点击事件中使A成为B的代理者;
//代理传值
- (void)btnDelegate{
NextViewController * nextVC = [[NextViewControlleralloc]init];
nextVC.delegate =self;
[self.navigationControllerpushViewController:nextVC animated:YES];
}
⑥在A控制器实现B协议中的代理方法;
//代理方法
- (void)delegateSend:(NSString *)strDelegate{
self.lblDelegate.text = strDelegate;
}
3.通知传值
(1)通知传值A-->B
①B是消息的接收者,要在A发送消息之前先建立好监听者,这时候就需要在B中重写init方法来达到这个目的,然后写受到通知后的操作,最后一定要记得移除监听者;
- (instancetype)init{
if (self = [superinit]) {
//创建监听者
[[NSNotificationCenterdefaultCenter] addObserver:selfselector:@selector(changeValue:)name:@"bobo"object:nil];
}
return self;
}
//通知传值
- (void)changeValue:(NSNotification *)notification{
NSDictionary * dict = notification.userInfo;
self.lblNotification.text = dict[@"key"];
}
//移除监听者
- (void)dealloc{
[[NSNotificationCenterdefaultCenter] removeObserver:selfname:@"bobo"object:nil];
}
②接下来就是在A的点击事件中添加发送消息即可。
//通知传值
- (void)btnNotification{
NextViewController * nextVC = [[NextViewControlleralloc]init];
NSNotification * notification = [[NSNotificationalloc]initWithName:@"bobo"object:nil userInfo:@{@"key":@"厦门博看文思"}];
//发送通知
[[NSNotificationCenterdefaultCenter] postNotification:notification];
[self.navigationControllerpushViewController:nextVC animated:YES];
}
(2)通知传值B-->A
①由B到A传值就更简单了,因为时间顺序上一定是先有A后有B,所以只要在A的viewDidLoad中建立一个监听者,然后实现监听事件,最后一处监听者就行
//在viewDidLoad中注册监听者
[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(changeValue:)name:@"bobo2"object:nil];
//收到通知后操作
- (void)changeValue:(NSNotification *)notification{
NSDictionary * dict = notification.userInfo;
self.lblNotification.text = dict[@"key"];
}
//移除监听者
- (void)dealloc{
[[NSNotificationCenterdefaultCenter]removeObserver:selfname:@"bobo2"object:nil];
}
- (void)btnNotification{
NSNotification * notification = [[NSNotificationalloc]initWithName:@"bobo2"object:nil userInfo:@{@"key":@"iOS"}];
//发送通知
[[NSNotificationCenterdefaultCenter] postNotification:notification];
[self.navigationControllerpopViewControllerAnimated:YES];
}
4.单例传值
//Teacher.h
#import <Foundation/Foundation.h>
@interface Teacher :NSObject<NSCopying,NSMutableCopying>
@property (nonatomic,strong) NSString * name;
@property (nonatomic,assign) int age;
+ (instancetype)shareInstance;
@end
#import "Teacher.h"
staticTeacher * teacher = nil;
@implementation Teacher
+ (instancetype)shareInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
teacher = [[Teacheralloc]init];//确保对象只被初始化一次
});
returnteacher;
}
+ (instancetype)allocWithZone:(struct_NSZone *)zone{
if (teacher ==nil) {
teacher = [[superallocWithZone:zone]init];
}
returnteacher;
}
- (instancetype)copyWithZone:(NSZone *)zone{
returnteacher;
}
- (id)mutableCopyWithZone:(nullableNSZone *)zone{
returnteacher;
}
@end
(1)单例传值A-->B
①单例传值只要在A中拿到单例对象,给对象属性赋值,都在跳转事件执行;
//单例传值
- (void)btnInstance{
NextViewController * nextVC = [[NextViewControlleralloc]init];
Teacher * teacher = [Teachernew];
teacher.name =@"厦门博看文思";
[self.navigationControllerpushViewController:nextVC animated:YES];
}
②在B界面拿到单例对象在给label赋值就行,在viewDidLoad写如下代码即可:
//单例传值
Teacher * teacher = [Teachernew];
self.lblInstance.text = teacher.name;
(2)单例传值B-->A
- (void)btnInstance{
//单例传值
Teacher * teacher = [Teachernew];
teacher.name =@"iOS";
[self.navigationControllerpopViewControllerAnimated:YES];
}
②但是A界面拿到单例值需要在viewWillAppear中去赋值:
- (void)viewWillAppear:(BOOL)animated{
// 单例传值
Teacher * teacher = [Teachernew];
self.lblInstance.text = teacher.name;
}
5.代码块传值
(1)代码块传值A-->B
@property (nonatomic,strong) void(^oneBlock)(NSString *) ;
- (instancetype)init{
if (self = [superinit]) {
//代码块传值a---->b
NextViewController * nextVC = self;
_oneBlock = ^(NSString *strBlock){
nextVC.lblBlock.text = strBlock;
};
}
return self;
}
③在A跳转到B的点击事件中调用代码块;
//代码块传值
- (void)btnBlock{
NextViewController * nextVC = [[NextViewControlleralloc]init];
//a---->b
if (nextVC.oneBlock) {//调用代码块前一定要先判断代码块是否为空,调用空的代码块会直接使系统奔溃
//跳会主线程掉用代码块
dispatch_async(dispatch_get_main_queue(), ^{
nextVC.oneBlock(@"厦门博看文思");
});
}
[self.navigationControllerpushViewController:nextVC animated:YES];
}
(2)代码块传值B-->A
@property (nonatomic,strong) void(^myBlock)(NSString *) ;
②不过这时候代码块赋值操作应该在A的点击事件中进行,因为需要在B中调用代码块传送一个字符串回到A中;//代码块传值
- (void)btnBlock{
NextViewController * nextVC = [[NextViewControlleralloc]init];
//b---->a
nextVC.myBlock = ^(NSString * strBlock){
self.lblBlock.text = strBlock ;
};
[self.navigationControllerpushViewController:nextVC animated:YES];
}
③在B跳转回A点击事件中调用代码块完成传值。
- (void)btnBlock{
//代码块传值b----->a
if (_myBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
_myBlock(@"iOS");
});
}
[self.navigationControllerpopViewControllerAnimated:YES];
}
6.界面跳转传值总结