事件传递机制

IOS面试题(UIView) ----- 事件传递机制 - 简书

面试题:
在以下场景中,父视图 ParentView 上有三个子视图 ViewAViewB 和 ViewCViewA 完全位于 ParentView 的范围内,ViewB 有一半在 ParentView 的范围内,而 ViewC 完全位于 ParentView 的范围之外。假设用户在 ViewAViewB 和 ViewC 的区域上触摸屏幕,请描述事件处理的顺序和机制,并解释哪些视图将有机会响应触摸事件。

追问1:
如果 ViewA 和 ViewB 能够响应触摸事件,但 ParentView 的 clipsToBounds 属性设置为 YES,那么当用户触摸 ViewB 位于 ParentView 范围之外的部分时,事件的处理情况会如何变化?

追问2:
在同样的设置下,如果 ViewAViewB 和 ViewC 都不处理触摸事件(即它们没有重写 touchesBegan:withEvent: 方法或者在重写的方法中调用了 super),请说明事件处理将如何沿着响应链传递。

追问3:
如果希望当用户触摸 ViewC 时,即使它位于 ParentView 范围之外,ViewC 也能响应事件,你将如何修改 ParentView 的代码或属性以实现这一行为?

提问4: 如果 ViewD 的 userInteractionEnabled 属性被设置为 NO,点击 ViewD 时会发生什么?

提问5: 如果 ViewD 被部分遮挡,例如被另一个视图 ViewE 遮挡,点击 ViewD 被 ViewE 遮挡的部分会发生什么?

提问6: 假设用户在 ViewA、ViewB 和 ViewC 的区域上触摸屏幕,请描述事件处理的顺序和机制,并解释哪些视图将有机会响应触摸事件。请回答每个view的传递和响应的顺序?

提问7:事件的传递和响应分别是深度优先遍历还是广度优先遍历?怎么证明?

viewithtag呢?

追问8:view上同时有事件和手势,点击view之后会怎么处理?

//9-12尚需验证

追问9:UIButton,UIControl区别?

追问10:
在一个复杂的视图层级结构中,ViewF覆盖在ViewG上,而且ViewF的userInteractionEnabled属性被设置为NO,那么当用户点击ViewF时,会发生什么?

追问11:
如果一个视图(比如ViewH)重写了touchesBegan:withEvent:方法,并在方法中没有调用super,那么这个视图的父视图会收到这个触摸开始事件吗?

追问12:
如果一个视图(比如ViewI)的alpha属性被设置为0(即完全透明),那么用户点击这个视图的区域时,触摸事件会被这个视图接收吗?

当用户在 ViewAViewB 和 ViewC 的区域上触摸屏幕时,iOS 系统会首先将触摸事件传递给主窗口,然后由主窗口沿着视图层级结构向下传递给最顶层的视图,即 ParentView。接下来:

  • 如果用户触摸在 ViewA 上,系统会将事件传递给 ViewA,因为 ViewA 完全位于 ParentView 内,并且是位于触摸点最上层的视图。如果 ViewA 能够处理该事件,它将成为第一响应者并对事件进行处理。
  • 如果用户触摸在 ViewB 的 ParentView 范围内的部分,系统同样会将事件传递给 ViewBViewB 将有机会处理该事件。
  • 如果用户触摸在 ViewC 上,因为 ViewC 完全位于 ParentView 范围之外,通常情况下 ViewC 不会接收到事件,除非特殊处理。事件不会传递给 ViewC,因为它不在 ParentView 的边界内。

追问1回答:

如果 ParentView 的 clipsToBounds 属性设置为 YES,这意味着 ParentView 会裁剪掉超出其边界的子视图部分。因此,即使 ViewB 能够响应触摸事件,当用户触摸 ViewB 在 ParentView 范围之外的部分时,触摸事件不会传递给 ViewB,因为该部分被裁剪掉了,对用户来说是不可见的。

追问2回答:

如果 ViewAViewB 和 ViewC 都不处理触摸事件,事件将会沿着响应链向上传递。以下是事件传递的顺序:

  • 对于 ViewA,事件首先传递到 ViewA,由于 ViewA 不处理该事件,事件将传递给 ParentView
  • 对于 ViewB 的 ParentView 范围内的部分,情况与 ViewA 相同,事件最终也会传递给 ParentView
  • ViewC 不会接收事件,因为它在 ParentView 范围之外。
  • 如果 ParentView 也不处理触摸事件,事件会继续沿响应链向上传递给包含 ParentView 的视图控制器,最终可能到达应用程序对象。

追问3回答:

要使位于 ParentView 范围之外的 ViewC 能够响应触摸事件,可以重写 ParentView 的 hitTest:withEvent: 方法。这个方法决定了哪个子视图应该接收触摸事件。通过在 hitTest:withEvent: 中添加检查,即使 ViewC 位于 ParentView 范围之外,我们也可以将事件手动转发给它:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {UIView *view = [super hitTest:point withEvent:event];if (view == nil) {// 将触摸点转换到 ViewC 的坐标系CGPoint pointInC = [self convertPoint:point toView:ViewC];// 检查触摸点是否在 ViewC 内if ([ViewC pointInside:pointInC withEvent:event]) {view = ViewC;}}return view;
}

这段代码首先调用 super 的 hitTest:withEvent: 方法来尝试按常规规则找到一个响应者。如果没有找到,并且触摸点正好在 ViewC 的范围内,则直接将 ViewC 作为响应者返回。这样即使 ViewC 不在 ParentView 的clipsToBounds 边界内,它也能接收和处理触摸事件。

提问4: 如果 ViewD 的 userInteractionEnabled 属性被设置为 NO,点击 ViewD 时会发生什么?

当 ViewD 的 userInteractionEnabled 属性被设置为 NO 时,ViewD 将不会接收任何触摸事件,也就是说,它会忽略所有的触摸。因此,点击 ViewD 时,触摸事件会穿透 ViewD 传递给它下面的视图。如果点击的是 ViewD 在 ViewA 上的半边,那么触摸事件会传递给 ViewA。如果点击的是 ViewD 在 ParentView 上的半边,那么触摸事件会传递给 ParentView

提问5: 如果 ViewD 被部分遮挡,例如被另一个视图 ViewE 遮挡,点击 ViewD 被 ViewE 遮挡的部分会发生什么?

假设 ViewE 是另一个子视图,它被添加到 ParentView 上,并且位于 ViewD 的上方,那么点击 ViewD 被 ViewE 遮挡的部分时,触摸事件首先会被 ViewE 接收,因为它位于最上层。如果 ViewE 能够接收并处理该触摸事件,那么事件处理到此结束。如果 ViewE 不处理事件(可能是因为 userInteractionEnabled 被设置为 NO,或者它选择不处理该事件),那么事件将会穿透 ViewE 并传递给它下面的视图,也就是 ViewD。如果 ViewD 同样选择不处理或者无法处理该触摸事件,那么事件最终会传递给 ViewD 的父视图 ParentView。如果 ViewD 被 ViewE 遮挡的部分正好是在 ViewA 之上,那么 ViewA 不会接收到该触摸事件,因为 ViewE 和 ViewD 都位于它的上方。

  • 上述问题中ViewD和ViewE是什么关系?兄弟关系,还是父子关系?

在上述问题中,ViewD 和 ViewE 被描述为都是 ParentView 的子视图,因此它们之间是兄弟关系(sibling relationship)。兄弟视图是相对于共同的父视图来说的——在这个例子中,ParentView 是它们共同的父视图。

视图层级结构中,兄弟视图的相对层次(谁在上,谁在下)取决于它们被添加到父视图的顺序。后添加的视图会覆盖在先添加的视图上面。如果 ViewE 被添加到 ParentView 上且在 ViewD 之后添加,那么 ViewE 将会覆盖 ViewD,处于更上层的位置。

当处理触摸事件时,iOS 首先会将事件传递给最上层的视图,即在视图层级中最接近用户的那个视图。如果 ViewE 覆盖了 ViewD,点击重叠区域时,系统会先把事件传递给 ViewE。只有当 ViewE 不能处理该事件时,事件才会向下穿透到下一层的 ViewD,或者如果 ViewD 也不处理,最终传递到 ParentView

  • ViewD不是ViewE的父视图,为什么ViewE 不处理事件时,事件将沿响应链传递给 ViewD?

在iOS的事件处理系统中,当一个视图(比如 ViewE)不处理一个事件时,这个事件通常会沿着响应链向上传递。响应链是由当前响应者(比如控件或视图)及其上级响应者(如父视图或视图控制器)组成的链式结构。

如果 ViewE 和 ViewD 是兄弟视图(即它们都是同一个父视图 ParentView 的子视图),事件的传递会有点不同。当用户在 ViewE 上触摸屏幕,而 ViewE 不处理该事件时,iOS会将事件传递给 ViewE 的父视图,也就是 ParentView,而不是直接传递给 ViewD

事件传递给 ParentView 后,ParentView 有机会接收并处理事件。如果 ParentView 也不处理该事件,那么 ParentView 的父视图或者它的视图控制器将有机会处理事件,如此类推,直到找到能够处理该事件的响应者。

然而,如果 ViewE 被设计成不应该拦截某些事件,或者在某些情况下应该把事件传递给特定的兄弟视图(比如 ViewD),开发者可以通过覆盖 hitTest:withEvent: 或 pointInside:withEvent: 方法来自定义事件的传递逻辑。通过这种方式,开发者可以改变默认的事件传递行为,使得事件在 ViewE 不处理时直接传递给 ViewD,而不是传递给它们共同的父视图 ParentView。然而,这种做法相对少见,因为它可能会违反视图层次结构的自然流程,因此需要谨慎使用。

提问6:

在iOS开发中,事件的传递和响应是遵循的原则是:从最底层的子视图开始,然后是其父视图,再到上一层的父视图,依次往上。同时,只有在父视图范围内的子视图才能接收到事件。具体到这个场景中:

  1. 如果用户在ViewA区域触摸屏幕,事件将首先传递给ViewA。ViewA作为第一响应者,有机会首先处理这个触摸事件。如果ViewA不处理这个事件,那么事件会向上传递,由ParentView进行处理。

  2. 如果用户在ViewB区域触摸屏幕,事件将首先传递给ViewB。同样,ViewB作为第一响应者,有机会首先处理这个触摸事件。如果ViewB不处理这个事件,那么事件会向上传递,由ParentView进行处理。

  3. 如果用户在ViewC区域触摸屏幕,因为ViewC完全位于ParentView的范围之外,所以它无法接收到触摸事件,也就没有机会处理这个事件。这个事件会直接由ParentView进行处理。

所以,事件的传递和响应顺序为:ViewA/ViewB -> ParentView。而ViewC因为完全位于ParentView的范围之外,因此无法接收和处理触摸事件。

以上是基于默认情况的分析,具体的事件处理还会受到具体代码的影响,例如是否重写了hitTest:withEvent:或者pointInside:withEvent:方法,或者是否设置了userInteractionEnabled、isHidden或者alpha属性等。

提问7: 

在iOS开发中,UIView的viewWithTag:方法使用的是深度优先遍历(Depth-First Search)。

这个方法会先检查自身的tag,如果匹配,就直接返回自身。如果不匹配,它会遍历其所有子视图,对每个子视图,都调用这个视图的viewWithTag:方法。这样就形成了一个递归的过程,最终形成的遍历路径是深度优先的。这个过程会一直到达视图树的最深层,如果还没有找到,就返回nil。

事件的传递和响应在iOS中确实是遵循深度优先遍历的原则。我们可以通过以下方式来验证这一点:

首先,我们可以创建一个视图层级结构,比如ParentView中包含ChildView1和ChildView2,ChildView1中又包含GrandChildView。然后我们在每一个视图的touchesBegan:withEvent:方法中打印一些信息。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {NSLog(@"%@", self);[super touchesBegan:touches withEvent:event];
}

当我们在GrandChildView上点击时,我们会看到控制台上首先打印出GrandChildView的信息,然后是ChildView1的信息,最后是ParentView的信息。这就是深度优先遍历的表现。

其次,深度优先遍历的原则也体现在UIView的hitTest:withEvent:方法中。在这个方法中,UIView会首先检查它的所有子视图,然后是它自己,最后是它的父视图。这个过程就是深度优先遍历。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {return nil;}if ([self pointInside:point withEvent:event]) {for (UIView *subview in [self.subviews reverseObjectEnumerator]) {CGPoint convertedPoint = [subview convertPoint:point fromView:self];UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];if (hitTestView) {return hitTestView;}}return self;}return nil;
}

这段代码首先检查自己的所有子视图(由于子视图数组是按照添加顺序排列的,所以这里使用reverseObjectEnumerator进行了反序遍历,确保后添加的子视图能够优先接收事件),然后是自己,最后返回nil表示事件需要继续向上传递给父视图。这个过程也是深度优先遍历。

追问8:

在iOS中,手势识别器和触摸事件可以并存。当一个视图同时添加了触摸事件和手势识别器时,当触摸开始,手势识别器和视图都可以接收到触摸事件。但是,手势识别器会首先接收并处理触摸事件,如果手势识别器识别出了手势,那么它就会“吞掉”这次触摸事件,视图的触摸事件处理方法就不会被调用;如果手势识别器没有识别出手势,那么这次触摸事件就会被视图的触摸事件处理方法接收并处理。

要注意的是,这种行为可以通过手势识别器的cancelsTouchesInView属性来改变。如果将该属性设置为NO,那么即使手势识别器识别出了手势,视图的触摸事件处理方法仍然会被调用。另外,还可以通过delegate来更精细的控制手势识别器和触摸事件的交互行为。

追问9:

???

追问10:
当用户点击ViewF时,由于ViewF的userInteractionEnabled属性被设置为NO,所以ViewF无法接收和处理触摸事件,触摸事件会直接传递给它的下一个响应者,也就是ViewG。

追问11:
如果ViewH重写了touchesBegan:withEvent:方法,并在方法中没有调用super,那么ViewH的父视图将不会收到这个触摸开始事件。因为在视图的事件处理方法中不调用super,会阻止事件的继续传递。

追问12:
即使视图ViewI的alpha属性被设置为0(完全透明),只要它的userInteractionEnabled属性为YES,用户点击这个视图的区域时,触摸事件仍然会被这个视图接收。在iOS中,视图的透明度不影响其接收触摸事件的能力。

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

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

相关文章

Geopandas以及CMakeList程序编写技巧

Geopandas官方文档 Geopandas官方文档 reset_index()函数 pandas库中的reset_index()函数是用于重新设置数据框索引的方法。 例如&#xff1a;当我对于文件数据进行了一系列操作后&#xff0c;例如设置了索引set_index&#xff0c;那么会导致数据的索引框发生变化&#xff…

医疗行业面临的网络安全挑战及应对策略

网络攻击已经成为各行各业日益严重的威胁&#xff0c;但医疗行业尤其容易受到影响。2023年&#xff0c;医疗领域的黑客事件占数据泄露的79.7%。 医疗领域 虽然患者、医疗服务提供者和决策者都对保护医疗信息有所关注&#xff0c;但关键的弱点在于提供电子健康记录&#xff08;…

【iOS】RunLoop详解(二)

RunLoop详解&#xff08;二&#xff09; RunLoop 的概念RunLoop 与线程的关系RunloopRunloop与线程的关系RunLoop对外的接口Runloop的Mode应用场景举例举例说明小结 RunLoop 的内部逻辑RunLoop的底层实现苹果用RunLoop实现的功能AutoreleasePool事件响应手势识别界面更新定时器…

[OpenGL高级光照] 阴影改善

目录 一 阴影失真 二 阴影改善 2.1 减小片段深度值 2.2 降低纹理 2.3 注意事项 三 消除Repeat的问题 3.1 让裁剪矩阵的立方体变大 ​3.2 利用采样范围重置 四 精度问题 本章节源码 点击此处 一 阴影失真 在上一篇中,实现了阴影效果之后,但是我们会发现阴影效果中地面…

draw.io 网页版二次开发(3):打包和部署(war包)

目录 一 说明 二 环境配置 1. 下载并安装 Apache Ant 2. 下载并安装JDK和JRE 3. 下载tomcat 4. Ant、JDK和JRE 环境变量的配置 三 draw.io打包 四 部署 五 最后 一 说明 应公司项目要求&#xff0c;需要对draw.io进行二次开发&#xff0c;并将html界面通过iframe 嵌…

Linux网络编程】传输层中的TCP和UDP(UDP篇)

【Linux网络编程】传输层中的TCP和UDP&#xff08;UDP篇&#xff09; 目录 【Linux网络编程】传输层中的TCP和UDP&#xff08;UDP篇&#xff09;传输层再谈端口端口号范围划分认识知名端口号netstatiostatpidofxargs UDP协议UDP协议端格式UDP的特点面向数据报UDP的缓冲数据UDP使…

【算法】竞赛常用知识之字符串1

前言&#xff1a; 本系列是学习了董晓老师所讲的知识点做的笔记 董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com) 动态规划系列&#xff08;还没学完&#xff09; 【算法】动态规划之线性DP问题-CSDN博客 【算法】动态规划之背包DP问题&#xff08;2024…

IP地址定位技术在网络安全中的作用

在当今数字化时代&#xff0c;网络安全已经成为企业、政府和个人面临的重要挑战之一。随着互联网的普及和网络攻击的增加&#xff0c;保护个人隐私和防止网络犯罪变得尤为重要。在这一背景下&#xff0c;IP地址定位技术作为网络安全的重要组成部分之一&#xff0c;发挥着关键作…

宝塔面板Java项目部署,五步轻松搞定

当涉及到正规的开发项目时&#xff0c;最终的上线部署是至关重要的一个环节。本文旨在以最简单便捷的方法来教你如何完成项目的部署工作。 1. SSH下载地址 项目完成后需要使用SSH终端进行项目部署&#xff0c;以确保安全的远程访问、和操作远程服务器。 Xshell (支持Windows系…

JDK8 新日期和时间 API

目录 一、旧版日期时间 API 存在的问题 二、新日期时间 API介绍 三、JDK 8的日期和时间类 LocalDate写法 LocalTime写法 LocalDateTime写法 对日期时间的修改&#xff0c;使用withAttribute方法 日期时间的比较 四、JDK 8的时间格式化与解析 五、JDK 8的 Instant 类 …

Elasticsearch 8.1官网文档梳理 -综述

积累 Elasticsearch 的常用知识&#xff0c;以及日常维护、学习用到的 API。因为相关内容太多&#xff0c;所以根据模块整理成了不同的文章&#xff0c;并在这里做汇总&#xff0c;整个系列的文章都会持续更新 目录 Elasticsearch 8.1官网文档梳理 - 四、Set up Elasticsearc…

树莓派遇到ping的奇葩问题解决办法

首先&#xff0c;先 ping raspberrypi 一下。获得树莓派的ip 然后开始配置静态ip winR后输入命令ipconfig查询当前网关ip 输入命令sudo nano /etc/dhcpcd.conf 在最末尾输入以下信息 -----------------------------------------------------------------------------------…

JavaScript中的事件监听

文章目录 事件监听事件类型鼠标触发类型案例——轮播图表单获得光标类型类型案例——点击搜索框获得下拉表单键盘触发类型效果展示表单输入触发类型案例——统计表单字符数量 事件对象常用属性环境对象回调函数 事件监听 事件&#xff1a;在编程时系统内发生的动作&#xff0c…

ubuntu bind9 主从配置

主配置 &#xff08;master&#xff09; # cat /etc/bind/named.conf.local zone "xxx.com" {type master;file "/var/lib/bind/xxx.com.hosts";also-notify {172.17.151.242; // 从IP};};# cat /var/lib/bind/xxx.com.hosts $ttl 3600 xxx.com. I…

多态的学习

1. &#x1f3f7;多态的概念 多态的概念&#xff1a; 通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会 产生出不同的状态。 举个栗子&#xff1a;比如买票这个行为&#xff0c;当普通人买票时&#xff0c;是全价买票…

将Flutter程序打包为ios应用并进行安装使用

如果直接执行flutter build ios: Building com.example.myTimeApp for device (ios-release)...════════════════════════════════════════════════════════════════════════════════No vali…

Web自动化 - selenium

文章目录 一、selenium的使用selenium的安装 二、元素1. 定位选择元素1.id 定位2. class_name 定位find_element 和 find_elements的区别3. TAG_NAME 定位4. 超链接 定位 2. 操控元素1. 查询内容2. 获取元素文本内容3. 获取元素属性 3. 浏览器常用操作API4. 鼠标操作 - perform…

[力扣题解]134. 加油站

题目&#xff1a;134. 加油站 思路 贪心法&#xff1b; 代码 暴力法 class Solution { public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int i, rest, index, size;size gas.size();for(i 0; i < size; i){// 从 i 开始//…

Python 全栈系列244 nginx upstream 负载均衡 踩坑日记

说明 最初是因为租用算力机(Python 全栈系列242 踩坑记录:租用算力机完成任务)&#xff0c;所以想着做一个负载均衡&#xff0c;然后多开一些服务&#xff0c;把配置写在nginx里面就好了。 一开始租用了一个3080起了一个服务&#xff0c;后来觉得速度不够快&#xff0c;再起了…

DOM 文档对象模型

一、DOM简介 1、什么是DOM DOM 文档对象模型简称&#xff0c;是W3C组织推荐的处理可扩展标记语言的标准编程接口 W3C已经定义了一系列的DOM接口&#xff0c;通过这些接口可以改变网页的内容、结构、样式 2、DOM树 DOM把以上内容都看做是对象 二、获取元素 获取页面元素&am…