Swift开发中UIVisualEffect层遮挡视图如何实现手势穿透交互
UIVisualEffectView作为UIView的子类,默认会拦截所有命中自身区域的触摸事件,不会自动向下传递给层级更低的视图,这就是覆盖全屏后下层CollectionView无法响应滑动、点击操作的根本原因。
方案1:自定义子类重写命中测试方法(推荐)
这个方案是最通用、兼容性最好的实现,既可以实现空白区域手势穿透,也不会影响你添加在毛玻璃层上的交互控件响应。
实现逻辑:重写hitTest(_:with:)方法,当触摸命中的对象是毛玻璃视图本身时,返回nil,告诉系统当前视图不处理这个事件,自动将事件转发给下层的视图;如果命中的是毛玻璃视图上添加的子控件,则正常返回对应子控件响应事件。
Swift实现代码:
class TouchThroughVisualEffectView: UIVisualEffectView { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let hitView = super.hitTest(point, with: event) return hitView == self ? nil : hitView } }
Objective-C实现代码:
@interface TouchThroughVisualEffectView : UIVisualEffectView @end @implementation TouchThroughVisualEffectView - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *hitView = [super hitTest:point withEvent:event]; return hitView == self ? nil : hitView; } @end
注意:添加到
UIVisualEffectView上的自定义交互控件必须放在它的contentView上,不要直接addSubview到UIVisualEffectView本身,否则会出现显示异常和交互问题。
方案2:关闭视图交互(仅适用于效果层无任何交互控件的场景)
如果你的毛玻璃层是纯展示用途,上面没有放任何需要响应点击、滑动的控件,可以直接关闭效果视图的用户交互属性,一行代码实现穿透:
// Swift visualEffectView.isUserInteractionEnabled = false
// Objective-C visualEffectView.userInteractionEnabled = NO;
这个方案的缺陷:一旦关闭交互,毛玻璃视图上所有的子视图都会失去响应能力,如果后续要在毛玻璃层加按钮、弹窗类交互元素,必须把这个属性改回true,否则控件完全无法触发操作。
避坑提示
不要通过修改UIVisualEffectView的alpha值、给layer设置透明度、修改backgroundColor的alpha通道的方式试图实现事件穿透,这类操作会直接破坏系统的毛玻璃渲染逻辑,出现效果发灰、闪烁、透明度异常等视觉问题。
内容的提问来源于stack exchange,提问作者V777

