一、事件起因

  最近在开发一版本的需求中,遇到一个问题,需要在一个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事件穿透的更多相关文章

  1. 支持事件穿透?使用pointer-events样式

    使用绝对定位元素,让元素A完全盖住元素B时,如何通过元素A来响应元素B的事件呢? 上图可以用下面的SVG代码来实现: <svg width="200" height=&quo ...

  2. 手机端 zepto tap事件穿透

    什么是事件穿透? 点击上面的一层时会触发下面一层的事件 ”google”说原因是“tap事件实际上是在冒泡到body上时才触发”,也就是Zepto的tap事件是绑定在document上的,所以会导致 ...

  3. iOS之事件穿透

    前言 小伙伴们在开发中是否遇到过这样的需求呢,一个控件的某个部分被另外一个控件遮挡住,当点击这个重叠部分时,需要响应被遮盖控件的点击事件,就如下图所示   当我们点击区域3时,响应蓝色按钮的点击事件, ...

  4. 如何让触摸事件穿透一个View

    如何让触摸事件穿透一个View 偶然间发现,如何屏蔽或者让触摸事件穿透一个view是一个很简单的事情. 现象: 源码: // // ViewController.m // UserInteractio ...

  5. [原创]实现多层DIV叠加的js事件穿透

    Flash里面有个很好的特性是,一个容器里,不存在实际对象的部分,不会阻拦鼠标事件穿透到下一层. 前端就不一样了,两个div层叠以后,上层div会接收到所有事件(即使这个div里面内容是空的,没有任何 ...

  6. vue中阻止事件穿透的方法

    默认情况下,事件在h5页面会穿透传递,比如一div里面套一个div,点击上层div,下层div也会响应 要阻止事件穿透,使用event.stopPropagation(); 代码示例: <div ...

  7. CSS实现事件穿透与背景图不跟随滚动条

    1. 事件穿透属性:pointer-events: none  // auto默认值.none:不捕捉target事件(实现穿透) 用途:当需要使用透明遮罩并且允许点击遮罩下方元素时,或需要使用背景容 ...

  8. IOS 多个ImageView图片层叠透明区域点击事件穿透

    经常用到多个透明图片层叠,但又需要获取不同图片的点击事件,本文实现图片透明区域穿透点击事件 实现人体各个部位点击 - (BOOL) pointInside:(CGPoint)point withEve ...

  9. UGUI 事件穿透规则

    UGUI事件分为两大类:点击和拖拽. 点击包括 pointerdown, pointerup. 拖拽包括 begindrag, drag, enddrag. 点击事件无穿透:只会被最上层UI响应,不会 ...

  10. 移动端touchstart事件穿透问题,解决方案

    [来源]:在开发移动端网站时,会经常徘徊在click和touchstart之间:因为touchstart虽然好用和快速响应:但是其缺点也是显而易见的,当我们大面积的使用touchstart的时候就会遇 ...

随机推荐

  1. redis 简单整理——哨兵原理[三十一]

    前言 简单介绍一下哨兵的原理. 正文 一套合理的监控机制是Sentinel节点判定节点不可达的重要保证,Redis Sentinel通过三个定时监控任务完成对各个节点发现和监控: 1)每隔10秒,每个 ...

  2. 批处理 if的知识点

    正文 批处理的if不同于我们在一些常规语言中的if,比如说c或者c# 或者 java等. 如果我们判断两个字符是否相等,我们使用 'a'=='a' 来判断. 但是如果是不相等却不能这样. gtr 大于 ...

  3. jenkins 持续集成和交付 —— 触发器(六)

    前言 什么是触发器呢?当某种条件达到的时候将会触发某个机关. 正文 jenkins 内置4种触发器: 1.触发远程构建 2.其他工程构建后触发 3.定时触发 4.轮询SCM 那么就来介绍一下这几种吧. ...

  4. Vue3.0里为什么要用 Proxy API 替代 defineProperty API

    一.Object.defineProperty 定义:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象 为什么能实现响应式 ...

  5. c#程序员必学清单

    必读书目:1. "Effective C#: 50 Specific Ways to Improve Your C#" by Bill Wagner2. "CLR via ...

  6. 三款免费强大的SSH工具食用指南

    食用清单 XShell FinalShell Electerm 食用方案 XShell 先说说老牌ssh工具XShell吧,用过很多年,说实话没啥别的毛病挺好用的.不过,还是有些地方有待加强,比如文件 ...

  7. easyx的使用,鼠标交互(3.0)

    本文从B站学习,借鉴: 学习视频地址:鼠标操作(旧版)_哔哩哔哩_bilibili

  8. 通过定时SQL提取阿里云API网关访问日志指标

    简介: 阿里云API网关服务提供API托管服务,提供了强大的适配和集成能力,可以将各种不同的业务系统API实现统一管理.API网关同时支持将API访问日志一键存储到日志服务,通过日志服务强大的查询分析 ...

  9. [Contract] 测试 Solidity 合约代码的两种方式 与 优缺点

    第一种,使用 Truffle 这类继承了测试工具的框架,只要编写 js 脚本就可以测试 web3 与合约的逻辑. 优点是完全可控,粒度够细,便于集成测试:缺点是需要花费一些时间编写测试脚本,不过值得. ...

  10. vue-公共组件的注册

    注册公共组件,在每个需要的页面直接输入文件名(<g-table/>)即可引用该组件 步骤: 1.新建components/global文件夹,以及components/global/g-t ...