背景
在開(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