美文网首页码源iOS寒哥管理的技术专题
iOS事件处理——Swift & Objective-C

iOS事件处理——Swift & Objective-C

作者: 猫爪 | 来源:发表于2015-10-23 11:30 被阅读2167次

    怎样选择最合适处理事件的控件

    • 当用户点击屏幕后,产生2个参数 一个touches 保存用户点击的 UITouch对象到 NSSet 中,和一个事件对象UIEvent ,并且加入到UIApplication 对象的事件处理Loop中队列

    • 由UIApplication查看,当前有没有堵塞,如果没有就将事件分发下去,一般是交给主窗口,keyWindow

    • 然后Window 会用 下面的这个方法

     //Swift
    func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? 
    
    //Objective-C
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
     
    
    • 该方法是用于查询当前最佳处理事件的控件,如果我们重写window的这个方法后,其他view的触摸事件没受影响,那么说明我们的思路是对的,废话不多说,代码带注释

    Swift

    override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    
            /*1.判断自己可不可以与用户交互满足一下几点
            1.self.userInteractionEnabled = YES 可以与用户交互
            2.self.hidden = NO 不是隐藏的
            3.self.alpha > 0.01 透明度大于 0.01 也就是用户可见
            */
    
            //下面是写的上面的反面
            if self.userInteractionEnabled == false || self.hidden == true || self.alpha <= 0.01 {
                return nil
            }
    
            /*2.这里查看这个点是不是在控件的bound上了,如果不在就返回nil */
    
            if self.pointInside(point, withEvent: event) == false { return nil }
    
    
            /*3.遍历 子控件,由于addSubView 本身是把图层加到另
            一个图层上面,导致图层的顺序是最后添加的在最上面,也就是
            上面说的,后面加入的控件可能会在盖住,前面加入的控件图
            层,而响应链是优先最上面的图层的,考虑到这个递归算法是 
            DFS 也就是深度优先,所以必须从后面的子控件遍历
            */
    
    
            //3.1得到子控制总个数
            let count = Int(self.subviews.count) - 1
    
    
            for var i = count ; i >= 0 ; --i  {
    
                //1.得到子控件
                let childView:UIView = self.subviews[i]
    
                /*2,将父控件的坐标点转换到子控件中的形式,其实就拿
                子控件在父控件的frame的x,y ,与用户触点的父控件的
                x,y,进行减法*/
    
                let childPoint = self.convertPoint(point, toView: childView)
    
                //3.继续递归
                let firstView = childView.hitTest(childPoint, withEvent: event)
    
                //4.发现不为空,就回传
                if let notEmpty = firstView {
                    return notEmpty
                }
                
            }
            //5.如果没知道合适的处理事件控件,就返回自己
            return self
            
        }
     
    
    • 上面说的自己写self.convertPoint(point, toView: childView)这方法,其实就是下面的算法
    func myConvertPoint(point:CGPoint, toView view:UIView) -> CGPoint {
    
            //1.拿到子控件的x,y
            let x = view.frame.origin.x
            let y = view.frame.origin.y
    
            //2.进行减法,并且返回
            return CGPointMake(point.x - x, point.y - y)
    
        }  
    

    Objective-C

    • 这里我的另一种思路,但是不提倡
    /*只要进入了这个方法,那就代表了这个视图,是可以与用户交互,包含那个点的。
    但是站在面向对象的角度,思考问题,”谁最清楚,控件能否被点击,
    点在不在自己身上,当然是自己",所以不提倡这样的写法*/
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
       
        int count = (int)self.subviews.count;
    
        for( int i = count - 1 ; i >= 0 ; --i ) {
    
            //1.拿到子控件
            UIView * childView = self.subviews[i];
    
            //2.转换坐标点
            CGPoint childPoint = [self convertPoint:point toView:childView];
    
            //3.判断子控件能不能与用户交互
            BOOL flagEnable = childView.userInteractionEnabled == YES || childView.hidden == NO || childView.alpha > 0.01;
    
            //4.判断点在不在子控件上
            BOOL flagInside = [childView pointInside:childPoint withEvent:event];
    
            //5.如果都可以,那么继续递归
            if (flagInside && flagEnable) {
    
                UIView *firstView = [childView hitTest:childPoint withEvent:event];
                //6.如果有,就回传
                if (firstView) {
                    return firstView;
                }
            }
        }
    
        //7.没找到最合适的就返回自己
        return self;
    }
    

    相关文章

      网友评论

      本文标题:iOS事件处理——Swift & Objective-C

      本文链接:https://www.haomeiwen.com/subject/asmthttx.html