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的时候就会遇 ...
随机推荐
- kubernetes集群最新版安装
原文地址:https://haiyux.cc/2022/09/21/k8s-install/ 虚拟机准备 我这里准备了三台虚拟机,分别部署一个master和两个node,操作系统位ubuntu 20. ...
- 力扣167(java&python)-两数之和 II - 输入有序数组(中等)
题目: 给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数.如果设这两个数分别是 numbers[in ...
- CF1481D AB Graph 题解
CF1481D AB Graph 题解 [思路] 首先有几个显而易见的东西. 如果存在两个点,他们之间的两条边字母相同,那么一定有解(在两个点之间跳.) 否则,这张图的邻接矩阵一定长成这样: * a ...
- KubeDL HostNetwork:加速分布式训练通信效率
简介:ubeDL 为分布式训练作业带来了 HostNetwork 网络模式,支持计算节点之间通过宿主机网络相互通信以提升网络性能,同时适应 RDMA/SCC 等新型高性能数据中心架构的网络环境,此外 ...
- [FAQ] panic: listen tcp :xxxx: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
在 Go 中运行服务之前的绑定端口这一步,如果端口号被占用了,那么会提示它只能使用一次. 换个端口号或者检查端口号的占用程序. Link:https://www.cnblogs.com/farwish ...
- [Py] Python dict 倒序操作
倒序操作很简单,使用 reversed( ) 方法,原本是 ['a', 'b', 'c'],倒序后就是 ['c', 'b', 'a'] Ref:在线运行Python代码 Refer:Python di ...
- dotnet 6 HttpClientHandler 和 SocketsHttpHandler 有什么差别
本文来告诉大家在 dotnet 6 的 HttpClientHandler 和 SocketsHttpHandler 两个类型有什么不同 在创建 HttpClient 时,可以在 HttpClient ...
- 2019-11-29-dotnet-remoting-使用事件
title author date CreateTime categories dotnet remoting 使用事件 lindexi 2019-11-29 10:20:1 +0800 2018-2 ...
- 习题8 #第8章 Verilog有限状态机设计-2 #Verilog #Quartus #modelsim
2. 设计一个"1001"串行数据检测器,其输入.输出如下: 输入x:000 101 010 010 011 101 001 110 101 输出z:000 000 000 010 ...
- [Java]线程生命周期与线程通信
[版权声明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://www.cnblogs.com/cnb-yuchen/p/18162522 出自[进步*于辰的博客] 线程生命周期与 ...