1.问题:之前在面试时被问到这样一个问题:如何扩大一个给定视图的点击区域,或者说如何让一个视图在当前视图外的子视图也能响应点击事件。
2.场景:可能大家不太理解这种问题是什么意思,现在给定场景。我们在开发中经常会遇到这样问题,上传图片的时候,我们选中图片后要删除选中的图片,会在右上角添加一个删除按钮。这时候删除按钮就会有一半,或者全部都出现在当前图片视图的bounds外,导致不好点或者不能点的情况出现,那就需要用到我们今天的命题,扩大视图的点击区域。
3.前提:在解决这个问题的时候,我们先要清楚两个东西
1.触摸时间的touch的产生顺序。一个APP中touch时间是如何产生的,当我们手指接触到屏幕时,通过硬件产生一个touch
然后以 touch -> UIApplication -> UIWindow -> UIViewController.View -> subviews -> ... ->最终找到最合适的响应者 or 丢弃
2.响应链:当我们找到最合适的响应者的时候,我的view就开始进行touch时间的响应。顺序恰恰和产生相反。
以 view -> supView ->... -> UIViewController -> UIWindow -> UIApplication or(丢弃)。
4.方法:hitTest,这个方法就负责进行touch事件的传递,当事件通过触摸点传过来之后,由这个类的对象通过hitTest方法判断传递给哪些subView,或者丢弃。一个事件会丢弃有四种情况
①:view.userinterfaceEnable == NO
②:view.hidden == YES
③:view.alpha < 0.05
④:点击区域 超出 view.bounds 之外。
所以我们不难推断hitTest的实现。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
if (self.hidden == NO || self.alpha < 0.05 || self.userInteractionEnabled == NO) {
//1.当满足这几个条件时,直接丢弃touch事件,不再向下分发。
return nil;
}else{
if (![self pointInside:point withEvent:event]) {
//2.如果点击point在视图之外,丢弃
return nil;
}else{
//3.分发给子视图
if (self.subviews.count > 0) {
for (UIView *subView in self.subviews) {
UIView *hitTestSubView = [subView hitTest:point withEvent:event];
return hitTestSubView;
}
}else{
return self;
}
}
}
}
5.解决,所以我们只需要在改变分发策略,让他在视图外同样分发给子视图就行了。
class HitTestView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if isHidden == true || alpha < 0.05 || isUserInteractionEnabled == false {
return super.hitTest(point, with: event)
}else{
if self.point(inside: point, with: event) {
return super.hitTest(point, with: event)
}else{
//1.有subView时交给subView 去响应
for subview in self.subviews{
let coverPoint = self.convert(point, to: subview)
return subview.hitTest(coverPoint, with: event)
}
//2.没有subView时交给自己来响应,也就是说你无论在哪儿点击都会响应(扩大点击区域)
//当然这里如果你想扩大到一定的返回,可以在此处加限制
let isResponse:Bool = false
if isResponse {
return self
}else {
return nil
}
//3.如果你不想当没有subView时就随便响应,j就返回nil
return nil
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print(#function)
}
}
6.参考:iOS hitTest