1、首先需要理解iOS事件处理机制
理解事件处理、响应者、响应者链概念
https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/understanding_event_handling_responders_and_the_responder_chain
当view超出其parentView边界时,响应事件的处理
https://developer.apple.com/library/content/qa/qa2013/qa1812.html
以上都是官方文档,建议认真研读!
2、结合Demo讲解
以下是我自己写得demo,测试验证响应者链事件传递。
- TargetBtn layout 超出superView bounds(图中蓝色区域部分),当点击超出superView区域时,并不会触发TargetBtn的点击事件。
- 在应对某些需求,如让TargetBtn超出superView也能响应点击事件,而又不改变layout。解决办法就是重写superView的
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
Demo代码
创建可重写点击事件的view:
@interface WilsonView : UIView
@property (weak, nonatomic) IBOutlet UIButton *targetBtn;
@end
@implementation WilsonView
- (void)layoutSubviews {
[super layoutSubviews];
[self setColorTargetBtn];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 将点击事件的点,从原有参考坐标系转换为目标视图的坐标系
CGPoint pointForTargetView = [self.targetBtn convertPoint:point fromView:self];
// 判断转换后的点是否位于目标视图中
if (CGRectContainsPoint(self.targetBtn.bounds, pointForTargetView)) {
// 返回目标视图
// !!! 注意目标视图只能是当前view的直接子视图或者间接自视图 !!!
return [self.targetBtn hitTest:pointForTargetView withEvent:event];
}
return [super hitTest:point withEvent:event];
}
#pragma mark - setSubviews
- (void)setColorTargetBtn {
self.targetBtn.layer.borderColor = [UIColor redColor].CGColor;
self.targetBtn.layer.borderWidth = 1.0f;
}
@end
在viewController中响应点击事件,验证结果:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *resultLab;
@property (weak, nonatomic) IBOutlet UIButton *otherBtn;
@property (weak, nonatomic) IBOutlet UIButton *topBtn;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setColorTopBtn];
[self setColorOtherBtn];
}
- (void)setColorTopBtn {
self.topBtn.layer.borderColor = [UIColor grayColor].CGColor;
self.topBtn.layer.borderWidth = 1.0f;
}
- (void)setColorOtherBtn {
self.otherBtn.layer.borderColor = [UIColor blueColor].CGColor;
self.otherBtn.layer.borderWidth = 1.0f;
}
#pragma mark - Action
- (IBAction)click100Button:(id)sender {
[self showText:@"target button"];
}
- (IBAction)clickTopButton:(UIButton *)sender {
[self showText:@"top button"];
}
- (IBAction)clickOtherButton:(UIButton *)sender {
[self showText:@"other button"];
}
- (void)showText:(NSString *)text {
self.resultLab.text = text;
}
- 当点击topBtn区域时,并不会响应targetBtn事件,也不会执行superView的hitTest:方法,此时响应者链并没有将事件传递给superView,因此也不会传递到targetBtn;
- 当点击除topBtn区域外的targetBtn区域时,会响应点击事件,我们重写hitTest:方法到达了目的。
- 分析:当点击除topBtn区域外的targetBtn区域时,superView会判断点击point是否位于自身的bounds内,若在它的范围内,则会把事件传递给targetBtn;若不在它范围内,则事件不会继续传递到superView的子view。我们做的事情就是告诉superView,当点击区域在它的范围内时,找到她的子view或更深层的子view来响应事件。