【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:self
                  forKeyPath:@"age"
                     options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
                     context: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 *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(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等,以后学到了会加以补充

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

了解Unity编辑器之组件篇Video(二)

Video Player组件&#xff1a;用于在游戏中播放视频的组件。它提供了一系列属性来控制视频的播放、显示和交互。 1.Source&#xff08;视频源&#xff09;&#xff1a;用于指定视频的来源。可以选择两种不同的视频源类型&#xff1a; &#xff08;1&#xff09;Vieo Clip&#…

STM32 点灯实现 7.18

嵌入式&#xff1a; 以应用为中心&#xff0c;以专用计算机为基础&#xff0c;软硬件可裁剪ARM A系列芯片&#xff1a;高端芯片&#xff0c;实现人机互动 R系列&#xff1a;实现时效性 M系列&#xff1a;低端芯片&#xff0c;控制硬件设备&#xff0c;灯&#xff0c;风扇....…

设备监测诊断与维护:优化工业生产效率的关键措施

在现代工业生产中&#xff0c;设备的稳定运行对于保障生产效率至关重要。设备监测、诊断和维护作为关键措施&#xff0c;能够帮助企业及时发现设备问题、诊断故障原因&#xff0c;并采取有效维护措施&#xff0c;从而降低生产中断风险&#xff0c;提高生产效率。本文将深入探讨…

Verilog 学习之路二——基础学习总结(摘取自菜鸟教程)

目录 1 Verilog 设计方法2. 基础语法2.1 格式2.2 数值表示数值种类表示方法 2.3 数据类型2.4 表达式 3. 编译指令4. 连续赋值5. 过程结构6 过程赋值7 时序控制8 语句块9 循环10 函数例子-数码管译码 1 Verilog 设计方法 Verilog 的设计多采用自上而下的设计方法&#xff08;to…

平台化的测试工具推荐|一站式测试平台RunnerGo

互联网行业的发展到今天越来越多的公司更加注重工作效率和团队协作&#xff0c;越来越多的产品也趋于平台化&#xff0c;平台化也更有利于提高团队效率&#xff0c;代码管理、持续构建、持续部署这些工具的发展都是非常超前的&#xff0c;它们对于团队协作的支持和工作效率的提…

vue中如何通过webpack-bundle-analyzer打包分析工具进行配置优化

vue中随着项目的不断功能迭代和开发&#xff0c;项目文件越来越多&#xff0c;项目的打包文件也越来越大。如何对打包文件进行分析优化&#xff0c;减小打包文件大小呢&#xff1f;可以通过webpack-bundle-analyzer 这个打包分析工具进行解决。 1、webpack-bundle-analyzer的安…

接口漏洞-WebService-wsdl+SOAP-Swagger+HTTP-WebPack

什么是接口&#xff1f; 接口就是位于复杂系统之上并且能简化你的任务&#xff0c;它就像一个中间人让你不需要了解详细的所有细节。像谷歌搜索系统&#xff0c;它提供了搜索接口&#xff0c;简化了你的搜索任务。再像用户登录页面&#xff0c;我们只需要调用我们的登录接口&am…

【go语言学习笔记】02 Go语言高效并发

文章目录 一、并发基础1. 协程&#xff08;Goroutine&#xff09;2. Channel2.1 声明2.2 无缓冲 channel2.3 有缓冲 channel2.4 关闭 channel2.5 单向 channel2.6 selectchannel 示例 二、同步原语1. 资源竞争2. 同步原语2.1 sync.Mutex2.2 sync.RWMutex2.3 sync.WaitGroup2.4 …

TypeError: can‘t convert np.ndarray of type numpy.object_.

在处理数据集的时候出现报错&#xff1a; TypeError: can’t convert np.ndarray of type numpy.object_. The only supported types are: float64, float32, float16, complex64, complex128, int64, int32, int16, int8, uint8, and bool. train_labels torch.tensor(train…

ES6基础知识七:你是怎么理解ES6中 Generator的?使用场景?

一、介绍 Generator 函数是 ES6 提供的一种异步编程解决方案&#xff0c;语法行为与传统函数完全不同 回顾下上文提到的解决异步的手段&#xff1a; 回调函数promise 那么&#xff0c;上文我们提到promsie已经是一种比较流行的解决异步方案&#xff0c;那么为什么还出现Gen…

Docker安装Nexus并配置Maven私服

1 准备工作 1 服务器已安装docker, docker各命令无报错 2 通过dockerhub查看nexus的版本信息&#xff0c;此次使用的镜像为&#xff1a;sonatype/nexus3&#xff0c;可以看到latest版本更前的的是3.58.0&#xff0c;我们这次就使用这个版本的nexus3. 2 开始安装 # 下载镜像 do…

Java中的队列

队列的理解 队列&#xff08;Queue&#xff09;是一种特殊的线性表&#xff0c;它只允许在表的前端进行删除操作&#xff0c;而在表的后端进行插入操作。 LinkedList类实现了Queue接口&#xff0c;因此我们可以把LinkedList当成Queue来用。 常用方法 实例 import java.util…

Go基础快速入门

目录 一、变量相关基础语法 1、变量的定义以及赋值 2、变量的交换 3、匿名变量 4、变量的作用域 二、常量 三、基本数据类型 1、常见数据类型 2、数据类型的转换 四、运算符 五、函数 函数高级用法 函数也是一个类型 函数也是一个变量&#xff0c;也可以赋值 高…

Jmeter接口自动化生成测试报告html格式

jmeter自带执行结果查看的插件&#xff0c;但是需要在jmeter工具中才能查看&#xff0c;如果要向领导提交测试结果&#xff0c;不够方便直观。 笔者刚做了这方面的尝试&#xff0c;总结出来分享给大家。 这里需要用到ant来执行测试用例并生成HTML格式测试报告。 一、ant下载安…

Opencv 细节补充

1.分辨率的解释 •像素&#xff1a;像素是分辨率的单位。像素是构成位图图像最基本的单元&#xff0c;每个像素都有自己的颜色。 •分辨率&#xff08;解析度&#xff09;&#xff1a; a) 图像分辨率就是单位英寸内的像素点数。单位为PPI(Pixels Per Inch) b) PPI表示的是每英…

【stm32L152】段码屏驱动注解、MX_LCD_Init()初始化失败的解决方法

文章目录 断码屏驱动补充MX_LCD_Init()驱动初始化失败 断码屏驱动补充 已经有大神写过较详细的教程&#xff1a;https://blog.csdn.net/CSDN_Gao_16/article/details/115463499&#xff0c;但这篇博文仍然比较抽象&#xff0c;我看了好多遍才看明白-_-||&#xff0c;为了节省和…

选择最佳安全文件传输方法的重要性

在数字化时代&#xff0c;文件的传输是商务、教育、科研、医学等领域不可或缺的工作流程。为了保障数据安全&#xff0c;选择最佳安全文件传输方法非常关键。在本文中&#xff0c;我们将探讨选择最佳安全文件传输方法的重要性。 第一、最佳安全文件传输方法可以保证文件内容不被…

Jmeter常见问题之URI异常

这篇文章介绍一下"http://"重复导致的URI异常问题&#xff0c;通常从浏览器地址栏复制url&#xff0c;直接粘贴到Jmeter的http请求的服务器地址中会默认带上“http://”&#xff0c;要将http://删除&#xff0c;只写IP地址&#xff0c;如下图&#xff1a; 否则&…

ESP-C3入门20. CentOS开发环境及Jenkins流水线

一、准备环境 CentOS8已经正常安装Jenkins 二、升级 cmake cmake 升到 3.16以上。 cmake --version # 安装 g sudo yum install gcc-c export CXXg# 安装 CMake 的依赖项 sudo yum install -y openssl-devel# 下载 CMake 源码并进行编译安装 wget https://github.com/Kitwa…

[ 容器 ] Harbor 私有仓库的部署与管理

目录 一、什么是Harbor二、Harbor的特性三、Harbor的构成四、Harbor 部署五、关于 Harbor.cfg 配置文件中有两类参数&#xff1a;所需参数和可选参数六、维护管理Harbor 一、什么是Harbor Harbor 是 VMware 公司开源的企业级 Docker Registry 项目&#xff0c;其目标是帮助用户…