【iOS】多界面传值

文章目录

  • 前言
  • 一、属性传值
  • 二、协议传值
  • 三、block传值
  • 四、KVO传值
  • 五、KVO的自动触发与手动触发
  • 六、通知传值
  • 总结


前言

在写网易云音乐以及3GShare包括后面的学生管理系统时,用到许多界面传值方法,特撰写博客记录目前学过的几种多界面传值方法

一、属性传值

属性传值是通过定义属性并设置值来实现传递数据的方式,多用于前一个页面向后一个页面传值
假设有两个视图控制器:ViewControllerA 和 ViewControllerB。

在 ViewControllerA.h 文件中定义一个属性:

#import <UIKit/UIKit.h>@interface ViewControllerA : UIViewController@property (nonatomic, strong) NSString *dataToPass;@end

在 ViewControllerA.m 文件中设置一个按钮点击事件,当点击按钮时,跳转到 ViewControllerB 并传递数据:

#import "ViewControllerA.h"
#import "ViewControllerB.h"@implementation ViewControllerA- (void)buttonTapped {ViewControllerB *viewControllerB = [[ViewControllerB alloc] init];viewControllerB.receivedData = self.dataToPass;[self.navigationController pushViewController:viewControllerB animated:YES];
}@end

在 ViewControllerB.h 文件中定义一个属性来接收从 ViewControllerA 传递过来的数据:

#import <UIKit/UIKit.h>@interface ViewControllerB : UIViewController@property (nonatomic, strong) NSString *receivedData;@end

在 ViewControllerB.m 文件中可以使用接收到的数据进行相应的操作:

> #import "ViewControllerB.h"
> 
> @implementation ViewControllerB
> 
> - (void)viewDidLoad {
>     [super viewDidLoad];
>     
>     // 使用接收到的数据
>     NSLog(@"Received data: %@", self.receivedData); }
> 
> @end

这样,当在 ViewControllerA 视图控制器中点击按钮,切换到 ViewControllerB 视图控制器时,就可以将 dataToPass 的值传递给 receivedData,并在 ViewControllerB 中使用接收到的数据。


二、协议传值

协议传值是通过定义协议和代理方法,在不同的视图控制器之间传递数据的方式。多用于后一个页面将数据回传给前一个页面

这里以3GShare注册界面回传账号密码给登录界面的协议传值作为示例并讲解其步骤:

  1. 定义协议:
    在发送数据的视图控制器(通常是源视图控制器)的头文件中定义一个协议,其中包含需要传递的数据的代理方法
    在这里插入图片描述
    这步需要我们定义协议的名称后再去定义回传数据的方法

  2. 声明代理属性:
    在发送数据的视图控制器的头文件中声明一个代理属性,用于保存代理对象。
    在这里插入图片描述

  3. 触发代理方法:
    在发送数据的视图控制器中,在适当的时机,触发代理方法,并将需要传递的数据作为参数传递给代理方法。
    在这里插入图片描述

  4. 实现代理方法:
    接收数据的视图控制器,也就是前一个控制器(通常是目标视图控制器)中,遵循协议并实现代理方法,以接收传递过来的数据。
    在这里插入图片描述

  5. 设置代理:(这是最重要的一步,很多人会忘了这步)
    在发送数据的视图控制器中,在跳转到接收数据的视图控制器之前,设置目标视图控制器的代理为当前视图控制器。
    在这里插入图片描述
    通俗的理解就是将后面的视图控制器的代理对象设为前一个视图控制器,让后一个为前一个代理,也就是手下替老板做事,然后将结果告诉老板


三、block传值

block传值同样也用于后面向前面传值,与协议传值有些相似的地方,但它使用代码块进行传值

  1. 定义 Block:
    在发送方(当前视图控制器)中定义一个 Block 属性,用于接收传递的数据。Block 的类型取决于你要传递的数据类型。
typedef void(^testblock)(NSString *);
@property(nonatomic, copy)testblock send;

typedef: 这是一个关键字,用于定义一个数据类型的别名,将后面的复杂类型定义简化为一个简洁的名字。
void(^testblock): 这部分是 Block 的类型定义,其中 testblock 是这个 Block 的别名,也就是我们定义的数据类型名。
(NSString *): 这是 Block 的参数列表,用括号括起来,表示 Block 接受一个 NSString 类型的参数。

这里需要注意的是:

typedef void(^testblock)(NSString *);:这是一个 Block 类型的定义,其中 testblock 是我们给这个 Block 类型起的别名,类似于自定义的数据类型。这个 Block 接受一个 NSString 类型的参数,并且没有返回值。

  1. 发送方设置 Block:
    在发送方视图控制器中设置 Block,将需要传递的数据作为 Block 的参数传入,并执行 Block。
    在这里插入图片描述
    在这段代码中,我将self.textField.text作为参数传入我的代码块,前一个视图控制器接收到的信息就是我传进去的参数
  2. 接收数据:
    在需要接受的地方将传回来的值进行使用,这里的(NSString *sendtext)中的sendtext就是回传回来的值
    在这里插入图片描述

这里给出block传值的实现动画
在这里插入图片描述


四、KVO传值

KVO(Key-Value Observing)是一种iOS编程中的一种观察者模式,用于监听对象属性值的变化。通过KVO,一个对象可以监视另一个对象的属性,当被监视的属性值发生变化时,会收到通知并执行相应的操作。

KVO实现步骤:

  1. 注册观察者:

在需要监听属性变化的对象(被观察者)中,调用addObserver:forKeyPath:options:context:
方法来注册观察者。这个方法告诉系统哪个对象要观察哪个属性,以及观察的选项和上下文。

假设我们有一个 Person 类,其中有一个属性 age,我们希望在另一个视图控制器中监听 age 的变化。

	self.person = [[Person alloc] init];self.person.age = 3;// 注册观察者[self.person addObserver:selfforKeyPath:@"age"options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOldcontext:NULL];

observer: 这是观察者对象,即要接收属性变化通知的对象。通常是当前视图控制器或其他感兴趣的对象。
keyPath: 要监听的属性的名称,以字符串表示。当该属性的值发生变化时,KVO 就会通知观察者。
options: 一个枚举值,用于指定监听的选项。这个参数可以设置为多个选项的组合,使用按位或(|)进行连接。常见的选项有:
NSKeyValueObservingOptionNew: 当属性的值发生变化时,提供新的属性值作为通知的参数。
NSKeyValueObservingOptionOld: 当属性的值发生变化时,提供旧的属性值作为通知的参数。
NSKeyValueObservingOptionInitial: 在添加观察者时,立即发送一次通知,提供当前属性的值作为通知的参数。
NSKeyValueObservingOptionPrior: 在属性值发生实际变化之前,先发送一次通知,提供旧的属性值作为通知的参数。
context: 这是一个指针类型的参数,用于传递额外的上下文信息。通常情况下可以传入 NULL,表示不需要传递上下文信息。如果你需要在观察者中处理一些额外的信息,可以使用自定义的指针类型来传递数据。

  1. 实现监听方法:
    在观察者对象中,实现一个监听方法- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context,监听方法会在被观察的属性发生变化时被调用。
// 实现监听方法,当 age 属性变化时会调用该方法
- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary<NSKeyValueChangeKey,id> *)changecontext:(void *)context {if ([keyPath isEqualToString:@"age"]) {// 获取旧值和新值NSNumber *oldAge = [change valueForKey:NSKeyValueChangeOldKey];NSNumber *newAge = [change valueForKey:NSKeyValueChangeNewKey];NSLog(@"old age: %@ --- new age: %@", oldAge, newAge);}
}

我们常用[change valueForKey:NSKeyValueChangeOldKey][change valueForKey:NSKeyValueChangeNewKey]来获得我们的新值与旧值

  1. 移除观察者:
    在观察者对象不再需要监听属性变化时,记得调用 removeObserver:forKeyPath: 方法来移除观察者,避免潜在的内存泄漏。
    在这里插入图片描述
    实现效果
    在这里插入图片描述在这里插入图片描述

五、KVO的自动触发与手动触发

一般来说,我们使用KVO来监听对象的属性,都是针对对象的单个属性,但如果我们想监听一个集合,上面的方法就不适用了。这是因为KVO的触发类型分为自动与手动,我们上面讲述的仅仅只是KVO的自动触发

  • 自动触发KVO通常适用于以下情况:

监听对象属性的变化:对于简单的对象属性,例如字符串、数字等,可以使用自动触发KVO。就像我们上面设置了一个age,这就是简单的对象属性,只需要在被监听属性声明前加上@property关键字,并使用nonatomic修饰符即可。当属性值发生变化时,KVO会自动发送通知给观察者。
使用@synthesize合成属性:在使用@synthesize合成属性时,如果指定了观察者,则合成的属性会自动触发KVO通知。

  • 手动触发KVO通常适用于以下情况:

监听集合属性的变化:集合属性不支持自动触发KVO通知,所以需要使用手动触发KVO来监听集合属性的变化。在修改集合属性之前调用willChangeValueForKey:方法,在修改完成后调用didChangeValueForKey:方法。
监听非对象类型属性:对于非对象类型的属性,例如C语言基本数据类型,由于它们不是对象,无法自动触发KVO。这时需要使用手动触发KVO通知来监听属性的变化。
自定义KVO通知:有时候我们可能需要在一些特定的场景下自定义KVO通知,这时可以使用手动触发KVO来实现。


这样一来我们知道了如果对象的设置的属性是集合的话,我们需要手动来触发我们的KVO,接下来讲一下原因:

当我们监听一个集合属性时(如NSMutableArray类型属性),KVO默认只会监听这个集合属性的变化,但不会监听集合中的元素的变化。也就是说,如果我们直接修改集合中的元素(比如使用addObject:方法),而没有通过集合属性的setter方法进行修改,KVO是无法察觉到集合中元素的变化的。

通俗的讲,我们的KVO的自动触发只适用于用setter方法修改的属性,也就是用点语法修改的属性都会自动触发我们的KVO,但是集合无法用点语法进行修改,那么这个时候就需要用到我们的手动触发


为了解决这个问题,我们需要在修改集合属性之前,调用willChangeValueForKey:方法,在修改之后,调用didChangeValueForKey:方法。这样做可以手动触发KVO通知,告诉KVO机制集合属性发生了变化,使得KVO能够正确地监听到集合中元素的变化。

  • 我们在原来的对象中添加一个集合属性
    在这里插入图片描述
  • 我们将简单的对象属性添加到我们的集合中,同时将我们的监听对象改为我们的集合
    在这里插入图片描述在这里插入图片描述
    此时我还没有去手动触发我们的KVO,我们试着运行一下程序:
    在这里插入图片描述
    我们的内容并没有变化,这也说明了我们并没有触发我们的KVO的监听方法
  • 接下来我们将实现手动监听
    在这里插入图片描述

willChangeValueForKey::在对象属性发生变化之前调用,用于通知KVO即将开始监听属性的变化。
didChangeValueForKey::在对象属性发生变化后调用,用于通知KVO属性的变化已经完成。
willChangeValueForKey 和 didChangeValueForKey 方法通过配对,这样可以通知观察者(监听者)该属性值即将发生变化和已经发生变化。这对于手动触发 KVO 监听非常重要,需要注意的是,这两种方法一定是配对出现的
在这里插入图片描述在这里插入图片描述

自此,我们实现了我们KVO对集合类型的监听


六、通知传值

通知传值是一种在不同对象之间进行信息传递的方式,它使用了通知中心来实现观察者模式,允许一个对象在发生改变时通知其他观察者对象。其可以用于跨多个界面传值

其步骤简单分为四步

  1. 创建并发送通知:
    首先,在发送者对象中创建一个通知,并指定通知的名称(通常使用字符串来表示)。可以通过NSNotification类或NSNotificationName宏来创建通知。需要传递信息给其他对象时,可以通过NSNotificationCenter的postNotificationName:object:userInfo:方法来发送通知。在发送通知时,可以附带一些额外的信息(如字典)作为通知的userInfo参数。
    在这里插入图片描述
    我们接下来详细看一下创建与发送通知的方法:
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

aName: 通知的名称,是一个字符串类型的参数。通过这个名称来标识不同的通知。发送通知时,需要指定要发送的通知的名称,接收通知时,也需要监听相应名称的通知。
anObject: 通知的发送者,是一个可选参数。通知可以有一个发送者,表示是哪个对象发送了这个通知。通常情况下,我们不需要传递发送者,可以传入nil。
aUserInfo: 通知的附加信息,是一个可选参数。可以通过这个字典传递一些额外的信息给接收通知的对象。通常情况下,我们在发送通知时,可以将一些需要传递的数据放入这个字典中。

这里需要注意的是我们的userInfo:后跟的参数是一个字典类型,我们通过传回一个字典,并通过查找字典的key来获取我们需要的数据

  1. 注册观察者:
    在接收者对象中,需要注册对某个通知感兴趣的观察者。这样,当通知被发送时,观察者就能接收到通知并做出相应的响应。使用NSNotificationCenter的addObserver:selector:name:object:方法来注册观察者。
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;

observer: 要注册的观察者对象。观察者对象将接收到与指定通知名称匹配的通知。通常,这个参数是当前对象,即要接收通知的对象。
selector: 观察者对象中用于处理通知的方法(函数)的选择器。这个方法必须带有一个参数,通常是 NSNotification 对象,用于接收传递的通知信息。该方法的声明通常形如 -(void)methodName:(NSNotification *)notification;。
name: 要观察的通知名称。这是一个字符串,用于标识要监听的通知。当发送通知时,只有与这个名称匹配的通知才会被发送给观察者。
object: 通知发送者的对象。如果设置为 nil,则会接收任何发送给指定名称的通知。如果设置为特定对象,只有该对象发送的与指定名称匹配的通知才会被发送给观察者。

这里有一点需要特别注意的就是要观察的通知名称必须于创造的通知的名称相同,否则无法接受来自通知的数据
在这里插入图片描述

  1. 接收通知:
    接收者对象需要实现一个方法,用于处理接收到的通知。这个方法是在观察者注册时通过selector参数指定的。当通知被发送时,通知中心会调用这个方法,并传递相关的信息给观察者。
    以我们上面的方法为例,receiveNotice:就是我们处理通知的方法
    在这里插入图片描述

  2. 移除观察者:
    在接收者对象被销毁之前,需要将其从通知中心中移除,避免出现潜在的内存泄漏。可以使用NSNotificationCenter的removeObserver:系列方法来移除观察者。

- (void)dealloc {// 移除通知观察者[[NSNotificationCenter defaultCenter] removeObserver:self];
}

接下来让我们看一下效果:
在这里插入图片描述
这样一来,我们就实现了通知传值

总结

多界面传值是iOS中十分重要的知识,笔者还有很多知识还没学到例如KVC等,以后学到了会加以补充

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/7825.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Docker】Docker部署私有仓库的配置及应用

文章目录 一、Docker-registry 搭建本地私有仓库1. Registry 的概念2. Registry 的部署过程 二、Docker-harbor 搭建私有仓库1. 什么是Harbor2. Harbor 的特性3. Harbor的构成4. Harbor 的部署过程4.1 安装 harbor4.2 创建项目并进行上传下载4.3 上传镜像到私有仓库4.4 从私有仓…

Vue--插槽

一、插槽-默认插槽 1.作用 让组件内部的一些 结构 支持 自定义 2.需求 将需要多次显示的对话框,封装成一个组件 3.问题 组件的内容部分&#xff0c;不希望写死&#xff0c;希望能使用的时候自定义。怎么办 4.插槽的基本语法 组件内需要定制的结构部分&#xff0c;改用**…

mysql的主键选择

一.没有定义主键有什么问题 如果定义了主键&#xff0c;那么InnoDB会使用主键作为聚簇索引如果没有定义主键&#xff0c;那么会使用第一非空的唯一索引&#xff08;NOT NULL and UNIQUE INDEX&#xff09;作为聚簇索引如果既没有主键也找不到合适的非空索引&#xff0c;那么In…

【数据挖掘】时间序列的傅里叶变换:用numpy解释的快速卷积

一、说明 本篇告诉大家一个高级数学模型&#xff0c;即傅里叶模型的使用&#xff1b; 当今&#xff0c;傅里叶变换及其所有变体构成了我们现代世界的基础&#xff0c;为压缩、通信、图像处理等技术提供了动力。我们从根源上理解&#xff0c;从根本上应用&#xff0c;这是值得付…

STM32MP157驱动开发——按键驱动(线程化处理)

文章目录 “线程化处理”机制&#xff1a;内核函数线程化处理方式的按键驱动程序(stm32mp157)编程思路button_test.cgpio_key_drv.cMakefile修改设备树文件编译测试 “线程化处理”机制&#xff1a; 工作队列是在内核的线程的上下文中执行的 工作队列中有多个 work&#xff0…

Git远程仓库使用方法

目录 介绍 详细教程 1、创建远程仓库 2、在本地初始化仓库 3、关联远程仓库 4、提交代码 5、拉取到本地仓库 6、提交到Git仓库 5、将本地代码推送到远程仓库 介绍 远程仓库在协同开发中起着关键的作用&#xff0c;它提供了一个中央存储库&#xff0c;使多个开发者能够…

Hadoop中HDFS的架构

一、Switch语句 语法规则&#xff1a; ①语句中的变量类型可以是byte、short、int或者char;从javaSE5开始支持枚举类型&#xff1b; javaSE7开始&#xff0c;switch支持String。 ②没有break时&#xff0c;后续case的语句都会执行 二、修饰符 访问修饰符 Java中&#xff0c…

【C++】vector类的模拟实现(增删查改,拷贝构造,赋值运算,深浅拷贝)

文章目录 前言一、 整体1.命名空间:2构造函数&#xff1a;1普通构造2迭代器构造3初始化字符构造4拷贝构造&#xff1a; 3析构函数 二、成员函数实现1.大小1当前大小(size())2总体容量(capacity()) 2.返回头尾迭代器1begin&#xff08;&#xff09;2end&#xff08;&#xff09;…

小程序如何修改商品

​商家可能会遇到需要修改产品信息的情况。无论是价格调整、库存更新还是商品描述的修改&#xff0c;小程序提供了简便的方式来帮助你们完成这些操作。下面是一些简单的步骤和注意事项&#xff0c;帮助你们顺利地修改商品。 一、进入商品管理页面 在个人中心点击管理入口&…

面向对象编程:从抽象到直观的探索之旅

文章目录 1. 面向过程与面向对象2. 面向对象的思想3. 类与对象的关系4. 类中包含什么&#xff1f;5. 类与对象的关系结语 在编程的世界里&#xff0c;我们常常会遇到两种不同的编程思想&#xff1a;面向过程和面向对象。面向过程是一种直观且容易理解的编程方式&#xff0c;而面…

矿井人员视频行为分析算法 opencv

矿井人员视频行为分析算法通过opencvpython网络模型技术&#xff0c;矿井人员视频行为分析算法实时监测人员的作业行为&#xff0c;并与安全标准进行比对&#xff0c;可以及时发现不符合安全要求的行为&#xff0c;预防事故的发生。OpenCV的全称是Open Source Computer Vision …

教师ChatGPT的23种用法

火爆全网的ChatGPT&#xff0c;作为教师应该如何正确使用&#xff1f;本文梳理了教师ChatGPT的23种用法&#xff0c;一起来看看吧&#xff01; 1、回答问题 ChatGPT可用于实时回答问题&#xff0c;使其成为需要快速获取信息的学生的有用工具。 从这个意义上说&#xff0c;Cha…

【N32L40X】学习笔记10-外部触发方式计数

定时器采用外部触发方式计数 也就是外部时钟源模式2 此模式由 TIMx_SMCTRL .EXCEN 选择等于 1。计数器可以在外部触发输入 ETR 的每个上升沿或下降沿 计数。 极性选择分频选择过滤选择选择外部时钟ETR模式 bsp_time_counter_ETR.h #ifndef _BSP_TIME_COUNTER_ETR_H_ #defi…

CSS布局之网格布局

网格布局&#xff08;Grid Layout&#xff09;是一种CSS布局模型&#xff0c;通过将页面划分为行和列的网格&#xff0c;可以更轻松地实现复杂的布局。 要使用网格布局&#xff0c;需要将元素的display属性设置为grid。然后&#xff0c;可以使用grid-template-rows和grid-temp…

一个月学通Python(二十五):使用缓存

专栏介绍 结合自身经验和内部资料总结的Python教程,每天3-5章,最短1个月就能全方位的完成Python的学习并进行实战开发,学完了定能成为大佬!加油吧!卷起来! 全部文章请访问专栏:《Python全栈教程(0基础)》 文章目录 专栏介绍使用缓存Django项目接入Redis为视图提供缓…

AI数字人:金融数字化转型的“关键先生”

今年年初ChatGPT的火热&#xff0c;在全球掀起一阵生成式AI&#xff08;AIGC&#xff09;热潮。国外的OpenAI、国内的百度等企业&#xff0c;都在AIGC上强力布局。 各种应用场景中&#xff0c;AIGC助力的数字人引起了市场注意。 事实上&#xff0c;数字人不是个新鲜事。早在1…

在Ubuntu 系统下开发GUI,用哪种开发工具比较好?

在Ubuntu系统下开发GUI&#xff0c;你可以考虑使用以下几种开发工具&#xff1a;Qt Creator&#xff1a;Qt Creator是一个跨平台的集成开发环境&#xff0c;专门用于开发基于Qt框架的应用程序。它提供了丰富的图形界面设计工具和代码编辑器&#xff0c;支持C和QML编程。Qt Crea…

UNI-APP_横屏切换竖屏出现样式混乱问题

app从竖屏页面1进入竖屏页面2&#xff0c;再进入横屏&#xff0c;再返回&#xff0c;再返回从新回到竖屏页面1&#xff0c;再次进入竖屏页面2&#xff0c;发现竖屏页面2的所有图片字体都被放大了。再返回竖屏1&#xff0c;再进入竖屏2&#xff0c;一切又恢复正常。 解决跳转横…

[NOI2008] 设计路线

题目描述 Z 国坐落于遥远而又神奇的东方半岛上&#xff0c;在小 Z 的统治时代公路成为这里主 要的交通手段。Z 国共有 n 座城市&#xff0c;一些城市之间由双向的公路所连接。非常神 奇的是 Z 国的每个城市所处的经度都不相同&#xff0c;并且最多只和一个位于它东边的 城市直…

el-upload文件上传(只能上传一个文件且再次上传替换上一个文件) vue3+vite+ts

组件&#xff1a; <template><el-upload class"upload-demo" v-model:file-list"fileList" ref"uploadDemo" action"/public-api/api/file" multiple:on-preview"handlePreview" :on-remove"handleRemove&quo…