页面传参

举报
SHQ5785 发表于 2024/05/14 08:44:10 2024/05/14
【摘要】 一、前言有关视图跳转详参博文《iOS开发进阶(十一):ViewController 控制器详解》。本篇博文继续讲解视图跳转参数如何传递与接收。界面跳转传值实际上就是将数据从一个界面传送给另一个界面,本文中的传值基于UINavigationController实现,首先在AppDelegate里面创建一个UINavigationController,把ViewController作为其根视图...

一、前言

有关视图跳转详参博文《iOS开发进阶(十一):ViewController 控制器详解》。本篇博文继续讲解视图跳转参数如何传递与接收。

界面跳转传值实际上就是将数据从一个界面传送给另一个界面,本文中的传值基于UINavigationController实现,首先在AppDelegate里面创建一个UINavigationController,把ViewController作为其根视图。代码如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];

    UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:[ViewController new]];

    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 = [UIColor blueColor];

UIButton *btnProperty = [UIButton buttonWithType:UIButtonTypeRoundedRect];

btnProperty.frame = CGRectMake(100,60, 175, 40);

[btnProperty setTitle:@"属性传值"forState:UIControlStateNormal];

[btnProperty addTarget:self action:@selector(btnProperty)forControlEvents:UIControlEventTouchUpInside];

[btnProperty setBackgroundColor:[UIColorgreenColor]];

[self.view addSubview:btnProperty];

[self.view addSubview:self.lblProperty];

另外给self.lblProperty做一个懒加载:

- (UILabel *)lblProperty{
    if (!_lblProperty) {

        _lblProperty = [[UILabel alloc]initWithFrame:CGRectMake(100,20, 175, 30)];

       _lblProperty.backgroundColor = [UIColor whiteColor];

    }

    return _lblProperty;

}

在NextViewController也加入相同代码,只需把界面颜色换一下区分开即可;

二、属性传值

属性传值即给相应的ViewController定义一个外部可用的属性,在界面跳转时传送相应的值,所有传值都可分为A–>B和B–>A,其中A为源视图,B为目标视图。

2.1 属性传值A–>B

通过属性由A传值到B不难理解,只需要在B界面定义一个属性,在A PUSH到B的时候把需要传送的内容赋值给B对应的属性即可,代码如下:

  1. 在NextViewController.h 定义一个属性(本文以字符串为例):

    @property (nonatomic,strong) NSString *strProperty2;
    
  2. 在ViewController中的点击事件做如下操作:

    //属性传值
    - (void)btnProperty{
        
        NextViewController *nextVC = [[NextViewControlleralloc]init];
    
        nextVC.strProperty2 = @"shq5785";
    
        [self.navigationController pushViewController:nextVC animated:YES];
    
    }
    
  3. 这时B控制器已经拿到了A传来的值,只需要在viewDidLoad做个赋值操作即可展现出传过来的字符串了:

    //属性传值
    self.lblProperty.text =self.strProperty2;
    

2.2 属性传值B–>A

属性由B到A传值会比A到B稍微复杂一点,不过也是分为三步:

  1. 给A定义一个外部可用属性:

    @property (nonatomic,strong) NSString *strProperty1;
    
  2. 在B跳转回A的点击事件中找到A控制器并给其属性赋值,这里拿到A控制器就不能跟A–>B那样直接新建一个控制器了,因为A控制器还存在,只是没有显示出来而已;所以应该通过导航栏控制器中获取当前A控制器:

    - (void)btnProperty{
        ViewController *VC =self.navigationController.viewControllers[0];
    
        VC.strProperty1 = @"iOS";
    
        [self.navigationController popViewControllerAnimated:YES];
    
    }
    
  3. 这时A中已经拿到了B传过来的值,但是要显示的话应该在viewWillAppear中处理:

    - (void)viewWillAppear:(BOOL)animated{
    	// 属性传值
        self.lblProperty.text = self.strProperty1;
    }
    

三、代理传值

3.1 代理传值A–>B

3.1 在A控制器的头文件申明协议:

@protocol ViewControllerDelegate <NSObject>

@optional

- (void)delegateSendToNext:(NSString * )strDelegate;

@end 
  1. @interface中添加遵循协议的代理属性

    @property (nonatomic,assign)id<ViewControllerDelegate> delegate;
    
  2. 在B的头文件中遵循A的协议;

    @interface NextViewController :UIViewController<ViewControllerDelegate>
    
  3. 在A跳转到B的点击事件中,让B界面成为A的代理,并调用代理方法;

//代理传值

- (void)btnDelegate{
   NextViewController *nextVC = [[NextViewController alloc]init];

  	self.delegate = nextVC;

	[self.delegate delegateSendToNext:@"shq5785"];

	[self.navigationController pushViewController:nextVC animated:YES];

}
  1. 在B控制器实现A协议中的方法完成传值。

    //在B控制器实现A控制器中的代理方法
    - (void)delegateSendToNext:(NSString * )strDelegate{
       self.lblDelegate.text = strDelegate;
    }
    

3.2 代理B–>A

  1. 在B控制器头文件申明协议;

    @protocol NextViewControllerDelegate <NSObject>
    
    @optional
    
    - (void)delegateSend:(NSString *)strDelegate;
    
    @end
    
  2. 在B控制器定义遵循协议的属性;

    @property (nonatomic,assign) id<NextViewControllerDelegate> delegate;
    
  3. 在B跳回A的点击事件中调用代理方法;

    - (void)btnDelegate{
        
        [self.delegate delegateSend:@"iOS"];
    
        [self.navigationController popViewControllerAnimated:YES];
    
    }
    
  4. A遵循B的协议;

    @interface ViewController ()<NextViewControllerDelegate>
    
  5. 在A跳转到B的点击事件中使A成为B的代理者;

    //代理传值
    
    - (void)btnDelegate{
        NextViewController *nextVC = [[NextViewController alloc]init];
    
        nextVC.delegate = self;
    
        [self.navigationController pushViewController:nextVC animated:YES];
    
    }
    
  6. 在A控制器实现B协议中的代理方法;

    //代理方法
    
    - (void)delegateSend:(NSString *)strDelegate{
       self.lblDelegate.text = strDelegate;
    
    }
    

四、通知传值

通知传值的核心部分,就是建立好监听者与发送者在时间上的关系,即一定要先有监听者再去发送通知,掌握好了这一点就可以玩转通知传值。

4.1 通知传值A–>B

  1. B是消息的接收者,要在A发送消息之前先建立好监听者,这时候就需要在B中重写init方法来达到这个目的,然后写收到通知后的操作,最后一定要记得移除监听者,否则会造成内存泄漏;

    - (instancetype)init{
       if (self = [superinit]) {
           //创建监听者
           [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeValue:) name:@"bobo" object:nil];
        }
        return self;
    }
    
    //通知传值
    - (void)changeValue:(NSNotification *)notification{
       NSDictionary * dict = notification.userInfo;
       self.lblNotification.text = dict[@"key"];
    }
    
    //移除监听者
    - (void)dealloc{
        [[NSNotificationCenter defaultCenter] removeObserver:self name:@"bobo" object:nil];
    }
    
  2. 接下来就是在A的点击事件中添加发送消息即可。

    // 通知传值
    - (void)btnNotification{
       NextViewController *nextVC = [[NextViewController alloc]init];
    
       NSNotification *notification = [[NSNotification alloc]initWithName:@"bobo" object:nil userInfo:@{@"key":@"shq5785"}];
    
     // 发送通知
     [[NSNotificationCenter defaultCenter] postNotification:notification];
     [self.navigationController pushViewController:nextVC animated:YES];
    }
    

4.2 通知传值B–>A

  1. 由B到A传值就更简单了,因为时间顺序上一定是先有A后有B,所以只要在A的viewDidLoad中建立一个监听者,然后实现监听事件,最后一处监听者就行

    //在viewDidLoad中注册监听者
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(changeValue:) name:@"bobo2" object:nil];
    
    //收到通知后操作
    
    - (void)changeValue:(NSNotification *)notification{
       NSDictionary *dict = notification.userInfo;
    
       self.lblNotification.text = dict[@"key"];
    
    }
    
    //移除监听者
    
    - (void)dealloc{
        [[NSNotificationCenter defaultCenter]removeObserver:self name:@"bobo2" object:nil];
    }
    
  2. 在B跳转回A的时候发送通知就行了。

    - (void)btnNotification{
        NSNotification * notification = [[NSNotification alloc]initWithName:@"bobo2" object:nil userInfo:@{@"key":@"iOS"}];
    
        //发送通知
    
        [[NSNotificationCenter defaultCenter] postNotification:notification];
    
        [self.navigationController popViewControllerAnimated:YES];
    
    }
    

五、单例传值

单例,就是从程序开始到程序结束不管中途怎么去实例化都只有一个实例对象,先创建一个单例(本文已Teacher类作为单例示例):

//Teacher.h

#import <Foundation/Foundation.h>

@interface Teacher :NSObject<NSCopying,NSMutableCopying>

@property (nonatomic,strong) NSString * name;

@property (nonatomic,assign) int age;

+ (instancetype)shareInstance;

@end
//Teacher.m
#import "Teacher.h"

staticTeacher *teacher = nil;

@implementation Teacher

+ (instancetype)shareInstance{

   static dispatch_once_t onceToken;

   dispatch_once(&onceToken, ^{
       teacher = [[Teacher alloc]init];//确保对象只被初始化一次
    });
    
    return teacher;

}

+ (instancetype)allocWithZone:(struct_NSZone *)zone{
   if (teacher ==nil) {
       teacher = [[superallocWithZone:zone]init];
    }

    return teacher;

}

- (instancetype)copyWithZone:(NSZone *)zone{
    return teacher;
}

- (id)mutableCopyWithZone:(nullableNSZone *)zone{
    return teacher;
}

@end

5.1 单例传值A–>B

  1. 单例传值只要在A中拿到单例对象,给对象属性赋值,都在跳转事件执行;

    //单例传值
    
    - (void)btnInstance{
        NextViewController *nextVC = [[NextViewController alloc]init];
    
       Teacher *teacher = [Teacher new];
    
        teacher.name = @"SHQ5785";
    
        [self.navigationController pushViewController:nextVC animated:YES];
    
    }
    
  2. 在B界面拿到单例对象在给label赋值就行,在viewDidLoad写如下代码即可:

    //单例传值
    
    Teacher *teacher = [Teacher new];
    
    self.lblInstance.text = teacher.name;
    

5.2 单例传值B–>A

  1. 由B界面跳回A界面也是跟A一样在跳转事件给单例对象赋值:

    - (void)btnInstance{
       //单例传值
       Teacher *teacher = [Teacher new];
    
       teacher.name =@"iOS";
    
       [self.navigationController popViewControllerAnimated:YES];
    
    }
    
  2. 但是A界面拿到单例值需要在viewWillAppear中去赋值:

    - (void)viewWillAppear:(BOOL)animated{
       // 单例传值
       Teacher *teacher = [Teacher new];
    
       self.lblInstance.text = teacher.name;
    
    }
    

六、代码块传值

代码块传值很好用很方便,但是理解上需要花点功夫,接下来咱就一步一步去分析完成;

6.1 代码块传值A–>B

  1. 代码块由A传值到B,需要先在B的头文件中定义一个外部可用代码块属性;

    @property (nonatomic,strong) void(^oneBlock)(NSString *) ;
    
  2. 然后B给代码块赋值,但是赋值一定要在A中调用之前完成,因此也需要重写B的init方法

    - (instancetype)init{
    
       if (self = [superinit]) {
           //代码块传值a-->b
           NextViewController *nextVC = self;
    
           _oneBlock = ^(NSString *strBlock){
                nextVC.lblBlock.text = strBlock;
           };
    
        }
    
        return self;
    
    }
    
  3. 在A跳转到B的点击事件中调用代码块;

    //代码块传值
    - (void)btnBlock{
        NextViewController * nextVC = [[NextViewController alloc]init];
    
       //a-->b
       if (nextVC.oneBlock) {//调用代码块前一定要先判断代码块是否为空,调用空的代码块会直接使系统奔溃
    		//跳回主线程调用代码块
            dispatch_async(dispatch_get_main_queue(), ^{
                 nextVC.oneBlock(@"SHQ5785");
            });
        }
    
        [self.navigationController pushViewController:nextVC animated:YES];
    
    }
    

6.2 代码块传值B–>A

  1. 代码块传值由B到A,也需要在B的头文件中定义一个外部可用代码块属性;

    @property (nonatomic,strong) void(^myBlock)(NSString *) ;
    
  2. 不过这时候代码块赋值操作应该在A的点击事件中进行,因为需要在B中调用代码块传送一个字符串回到A中;

    //代码块传值
    - (void)btnBlock{
        NextViewController *nextVC = [[NextViewController alloc] init];
    
        //b-->a
        nextVC.myBlock = ^(NSString *strBlock){
           self.lblBlock.text = strBlock ;
        };
    
        [self.navigationController pushViewController:nextVC animated:YES];
    
    }
    
  3. 在B跳转回A点击事件中调用代码块完成传值。

    - (void)btnBlock{
        //代码块传值b-->a
       if (_myBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
               _myBlock(@"iOS");
            });
        }
    
        [self.navigationController popViewControllerAnimated:YES];
    
    }
    

界面跳转传值总结
(1)一般A–>B使用属性传值单例传值一般可用于A跳转到多个界面,多个界面都需要用到A的值的情况;
(2)一般B–>A使用代码块或者代理传值更为方便,使用通知传值也很方便,不过一般情况不推荐使用通知传值通知传值存在内存泄漏风险,用多了有可能导致系统奔溃。

七、拓展阅读

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。