• 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

  • 相关阅读:
    Angular之filter学习
    Jupyter notebook 安装,初步使用
    Python排序算法
    获取相对于控件的鼠标坐标
    使用PYTHON实现docx文档的读写
    MATLAB 物体识别算法说明:vision.ForegroundDetector, vision.BlobAnalysis
    MATLAB 例子研究 Motion-Based Multiple Object Tracking
    MATLAB 图像处理——Contrast Enhancement Techniques
    MATLAB 图像操作基础
    MATLAB 图像分类 Image Category Classification Using Bag of Features
  • 原文地址:https://www.cnblogs.com/doudouyoutang/p/9774840.html
Copyright © 2020-2023  润新知