UINavgationBar事件穿透
一、事件起因
最近在开发一版本的需求中,遇到一个问题,需要在一个ViewController的顶部,UINavgationBar的下面放置一个View,这个View需要能够正常收到事件
将我们的View放到这个位置之后,发现底部的View、按钮等无法接受到响应
二、解决思路
1)第一次想到的是事件转发,如果控制了事件分发是不是就可以将事件想发给谁就发给谁了呢?
我们先回忆一下,事件分发的过程:
1、首先用户触摸屏幕产生触摸事件(此外还有Motion Event,Remote Event),手机系统驱动收到这个触摸事件
2、系统产生中断,进入响应触摸事件的终端程序,此时肯定是系统将事件封装为UIEvent,并发送给最前台的进程
3、进程收到这个事件之后,将其发给UIApplication进行处理
4、UIApplication 收到这个UIEvent事件之后,会进行一个事件的分发,决定将这个事件发给最终的响应者进行处理
这里涉及到两部分,建立事件响应链、沿着事件响应链处理事件
建立事件响应链,其实是寻找最合适的事件响应者(responder)的过程,步骤大概如下
首先UIApplication对象将事件发给UIWindow,UIWindow会调用从UIView 继承的 hitTest方法返回第一响应者
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
hitTest 是一个递归当用的方法,找到触摸事件的第一响应者,流程是,对Window下面的所有子View调用hitTest方法,
如果触摸点 point Inside子view中,那么相当于找到下一级,再次进行递归寻找,直到找到最终的响应者。
以上是事件寻找响应者的过程、如果第一响应者不能处理这个事件,会将这个事件沿着nextResponder逐步向上传递,进行处理
5、当找到第一响应者之后,会有响应的回调
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
如果这个响应者上绑定有手势识别器,那么手势识别器会对这个事件进行延迟、等识别
这就是UIScrollView上面按钮响应慢的原因,因为第一个touchBegan来了之后,手势识别器需要后面的事件一起判断是滑动还是点击
2)如何实现UINavgationBar事件穿透功能
我们可以通过修改寻找响应者的逻辑,来将事件抛回上层,再次递归到底层的View来实现,具体如下
首先我们继承一个UINavgationBar,修改hitTest的流程
//穿透点击事件 @objc class UICustomTouchNavigationBar: UINavigationBar { @objc var passThroughEvent:Bool = false override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let v = super.hitTest(point, with: event)
if (passThroughEvent && v?.ignoreEvent ?? false)
{
//启用事件穿透的时候
return nil
}
return v;
}
} var UIViewIgnoreEventKey = "UIViewIgnoreEventKey" @objc extension UIView { public var ignoreEvent:Bool {
get{
guard let value = objc_getAssociatedObject(self,
&UIViewIgnoreEventKey)
as? Bool else {
return false
}
return value
} set{ objc_setAssociatedObject(self,
&UIViewIgnoreEventKey,
newValue,
objc_AssociationPolicy.OBJC_ASSOCIATION_ASSIGN)
}
} }
我们要做的是,在UINavgationBar上面的按钮等收到事件的时候,我们不忽略;在空白的地方收到事件的时候,我们直接返回nil进行忽略
在初始化导航的时候,传入我们自定义的NavBar
- (instancetype)initWithNavigationBarClass:(nullable Class)navigationBarClass toolbarClass:(nullable Class)toolbarClass NS_AVAILABLE_IOS(5_0);
因为UINavgationBar是属于每个ViewController,也就是独立的,所以我们只需要在ViewDidLoad中启用事件穿透,并且把UINavgationBar的子View中的时间忽略标志打开,那么事件将自动传递到底层中。
三、附言
1)图片来自
https://www.jianshu.com/p/74a2f44840fa
https://www.jianshu.com/p/4155c9ffe1a8
UINavgationBar事件穿透的更多相关文章
- 支持事件穿透?使用pointer-events样式
使用绝对定位元素,让元素A完全盖住元素B时,如何通过元素A来响应元素B的事件呢? 上图可以用下面的SVG代码来实现: <svg width="200" height=&quo ...
- 手机端 zepto tap事件穿透
什么是事件穿透? 点击上面的一层时会触发下面一层的事件 ”google”说原因是“tap事件实际上是在冒泡到body上时才触发”,也就是Zepto的tap事件是绑定在document上的,所以会导致 ...
- iOS之事件穿透
前言 小伙伴们在开发中是否遇到过这样的需求呢,一个控件的某个部分被另外一个控件遮挡住,当点击这个重叠部分时,需要响应被遮盖控件的点击事件,就如下图所示 当我们点击区域3时,响应蓝色按钮的点击事件, ...
- 如何让触摸事件穿透一个View
如何让触摸事件穿透一个View 偶然间发现,如何屏蔽或者让触摸事件穿透一个view是一个很简单的事情. 现象: 源码: // // ViewController.m // UserInteractio ...
- [原创]实现多层DIV叠加的js事件穿透
Flash里面有个很好的特性是,一个容器里,不存在实际对象的部分,不会阻拦鼠标事件穿透到下一层. 前端就不一样了,两个div层叠以后,上层div会接收到所有事件(即使这个div里面内容是空的,没有任何 ...
- vue中阻止事件穿透的方法
默认情况下,事件在h5页面会穿透传递,比如一div里面套一个div,点击上层div,下层div也会响应 要阻止事件穿透,使用event.stopPropagation(); 代码示例: <div ...
- CSS实现事件穿透与背景图不跟随滚动条
1. 事件穿透属性:pointer-events: none // auto默认值.none:不捕捉target事件(实现穿透) 用途:当需要使用透明遮罩并且允许点击遮罩下方元素时,或需要使用背景容 ...
- IOS 多个ImageView图片层叠透明区域点击事件穿透
经常用到多个透明图片层叠,但又需要获取不同图片的点击事件,本文实现图片透明区域穿透点击事件 实现人体各个部位点击 - (BOOL) pointInside:(CGPoint)point withEve ...
- UGUI 事件穿透规则
UGUI事件分为两大类:点击和拖拽. 点击包括 pointerdown, pointerup. 拖拽包括 begindrag, drag, enddrag. 点击事件无穿透:只会被最上层UI响应,不会 ...
- 移动端touchstart事件穿透问题,解决方案
[来源]:在开发移动端网站时,会经常徘徊在click和touchstart之间:因为touchstart虽然好用和快速响应:但是其缺点也是显而易见的,当我们大面积的使用touchstart的时候就会遇 ...
随机推荐
- MVC 下拉选项实现的几种方式
主要介绍4种方式 硬编码方式: ViewBag.hard_value = new List<SelectListItem>() { new SelectListItem(){Value=& ...
- spring boot oauth2 取消认证
最近有一个项目需要从微服务中抽离,但是因为调用的包里关联了认证所以就算抽离处理还是会进oauth2默认的登入页面: @SpringBootApplication(exclude = {EurekaCl ...
- 元素类型 “item” 相关联的 “name” 属性值不能包含 ‘<’ 字符
Android构建时报错: app:lintVitalRelease[Fatal Error] :3:214: 与元素类型 "item" 相关联的 "name" ...
- Java面试题:为什么HashMap不建议使用对象作为Key?
HashMap 是一种基于哈希表的动态数据结构,它允许使用任意不可变对象作为键(key)来存储和检索数据.然而,在某些情况下,使用对象作为 HashMap 的键可能会遇到一些问题. 首先,我们需要明确 ...
- 容器基础-- namespace,Cgroup 和 UnionFS
Namespace 什么是 Namespace ? 这里的 "namespace" 指的是 Linux namespace 技术,它是 Linux 内核实现的一种隔离方案.简而言之 ...
- 很强!4.7k star,推荐一款Python工具,可实现自动化操作!!
1.介绍 在日常工作中,肯定会遇到一些重复性的工作,不管是点击某个按钮.写东西,打印东西,还是复制粘贴拷贝资料之类的,需要进行大量的重复操作.按键精灵大家都听说过,传统的方式,大家可以使用按键精灵将操 ...
- win11设置笔记本合盖不睡眠
win11设置笔记本合盖不睡眠 直接搜索控制面板,类型选择大图标,找到电源选项 点击进入电源选项,然后点击选择电源按钮的功能 然后就可以看到一个关闭盖子时,设置成不采取任何操作 然后就可以了
- 力扣196(MySQL)-删除重复的电子邮箱(简单)
题目: 表: Person 编写一个 SQL 删除语句来 删除 所有重复的电子邮件,只保留一个id最小的唯一电子邮件. 以 任意顺序 返回结果表. (注意: 仅需要写删除语句,将自动对剩余结果进行查询 ...
- 力扣525(java&python)-连续数组(中等)
题目: 给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度. 示例 1: 输入: nums = [0,1]输出: 2说明: [0, 1] 是具有相 ...
- 力扣341(java)-扁平化嵌套列表迭代器(中等)
题目: 给你一个嵌套的整数列表 nestedList .每个元素要么是一个整数,要么是一个列表:该列表的元素也可能是整数或者是其他列表.请你实现一个迭代器将其扁平化,使之能够遍历这个列表中的所有整数. ...