iOS swift UIView及其Subview通過(guò)透明度的判讀進(jìn)行事件穿透

背景

在開(kāi)發(fā)過(guò)程中會(huì)有這樣一種場(chǎng)景:在一個(gè)頁(yè)面A上彈出了一個(gè)透明或者半透明的view B,希望在點(diǎn)擊ViewB的透明或者半透明區(qū)域的時(shí)候,將點(diǎn)擊事件透?jìng)鹘o下層頁(yè)面A。廢話不說(shuō)啦,請(qǐng)看代碼

       override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
            let alpha = alphaOfPoint(point: point)
            // 處于當(dāng)前view及sub view的點(diǎn)擊位置顏色的alpha值大于閾值,則事件不穿透,否則就透?jìng)?            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:方法大致處理流程是這樣的:

  • 首先調(diào)用當(dāng)前視圖的pointInside:withEvent:方法判斷觸摸點(diǎn)是否在當(dāng)前視圖內(nèi):
  • 若pointInside:withEvent:方法返回NO,說(shuō)明觸摸點(diǎn)不在當(dāng)前視圖內(nèi),則當(dāng)前視圖的hitTest:withEvent:返回nil
  • 若pointInside:withEvent:方法返回YES,說(shuō)明觸摸點(diǎn)在當(dāng)前視圖內(nèi),則遍歷當(dāng)前視圖的所有子視圖(subviews),調(diào)用子視圖的hitTest:withEvent:方法重復(fù)前面的步驟,子視圖的遍歷順序是從top到bottom,即從subviews數(shù)組的末尾向前遍歷,直到有子視圖的hitTest:withEvent:方法返回非空對(duì)象或者全部子視圖遍歷完畢。

最后附上Demo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容