iOS swift UIView及其Subview通过透明度的判读进行事件穿透

背景

在开发过程中会有这样一种场景:在一个页面A上弹出了一个透明或者半透明的view B,希望在点击ViewB的透明或者半透明区域的时候,将点击事件透传给下层页面A。废话不说啦,请看代码

       override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
            let alpha = alphaOfPoint(point: point)
            // 处于当前view及sub view的点击位置颜色的alpha值大于阈值,则事件不穿透,否则就透传
            if alpha > DuPoplayerWKWebview.alphaThreshold {
                return true
            }else {
                return false
            }
        }
        
        func alphaOfPoint(point: CGPoint) -> CGFloat {
            return alphaOfPointFromLayer(point: point)
        }
        
        
        func alphaOfPointFromLayer(point: CGPoint) -> CGFloat {
            var pixel = [UInt8](repeatElement(0, count: 4))
    //        var pixel: [UInt8] = [0, 0, 0, 0]
            let colorSpace = CGColorSpaceCreateDeviceRGB()
            let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
            context?.setBlendMode(.copy)
            context?.translateBy(x: -point.x, y: -point.y)
            if let context = context {
                layer.render(in: context)
            }
            let alpha = CGFloat(pixel[3]) / CGFloat(255.0)
            
            return alpha
        }
        
        override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
            if self.point(inside: point, with: event) {
                return super.hitTest(point, with: event)
            }
            guard isUserInteractionEnabled, !isHidden, alpha > 0 else {
                return nil
            }

            for subview in subviews.reversed() {
                let convertedPoint = subview.convert(point, from: self)
                if let hitView = subview.hitTest(convertedPoint, with: event) {
                    return hitView
                }
            }
            return nil
        }

hitTest:withEvent:方法大致处理流程是这样的:

  • 首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内:
  • 若pointInside:withEvent:方法返回NO,说明触摸点不在当前视图内,则当前视图的hitTest:withEvent:返回nil
  • 若pointInside:withEvent:方法返回YES,说明触摸点在当前视图内,则遍历当前视图的所有子视图(subviews),调用子视图的hitTest:withEvent:方法重复前面的步骤,子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图的hitTest:withEvent:方法返回非空对象或者全部子视图遍历完毕。

最后附上Demo

你可能感兴趣的:(iOS swift UIView及其Subview通过透明度的判读进行事件穿透)