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;
}
}
}
}
由上面的hittest机制可以知道,如果我们想扩大一个视图的点击范围,或者让一个超出父视图的子视图可以点击,我们既可以通过重写hittest方法实现,即在hittest 方法中去掉pointinside方法的判断,同时也可以通过重写pointinside 方法来实现
重写任何一个方法都可以实现这两种功能
以前写过通过hittest方法来实现的文章 点击这里
所以这里我们只介绍通过重写pointinside 方法分别实现扩大点击范围和超出父视图可以点击
其实不管是重写hittest 或者是 重写point inside ,不管是
扩大点击范围,或者是超出父视图可以点击,本质上都是
修改系统的判断一个点是否在视图的的判断逻辑
重写hittest, 扩大点击范围我们就去掉系统直接掉用pointinside方法,
改为判断扩大后的rect 是否包含point,超出父视图可以点击
我们就直接将point转换为相对于子视图的point,然后让子视图自己掉用hittest, 看是否返回view
代码如下
//
// LBHittestButton.m
// TEXT
//
// Created by mac on 2024/6/1.
// Copyright © 2024 刘博. All rights reserved.
//
#import "LBHittestButton.h"
@interface LBHittestButton ()
@property (nonatomic, strong) UIButton *subButton;
@end
@implementation LBHittestButton
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self addSubview:self.subButton];
}
return self;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint pointInSubbutton = [self convertPoint:point toView:self.subButton];
if ([self.subButton hitTest:pointInSubbutton withEvent:event]) {
return [self.subButton hitTest:pointInSubbutton withEvent:event];
}
CGRect rect = UIEdgeInsetsInsetRect(self.bounds, self.expandEdgeInset);
if (CGRectContainsPoint(rect, point)) {
return self;
}
return [super hitTest:point withEvent:event];
}
- (void)clickSubButton
{
NSLog(@"哈哈哈点击小按钮小按钮");
}
#pragma mark - lazy load
- (UIButton *)subButton
{
if (!_subButton) {
_subButton = [UIButton buttonWithType:UIButtonTypeCustom];
_subButton.frame = CGRectMake(100, 140, 40, 40);
_subButton.backgroundColor = [UIColor redColor];
[_subButton addTarget:self action:@selector(clickSubButton) forControlEvents:UIControlEventTouchUpInside];
}
return _subButton;
}
@end
重写point inside ,如果是扩大点击范围,我们就判断新的rect是否包含point, 如果是子视图超出父视图可以点击,我们就将point转换为相对于子视图的point,然后判断是否在子视图中
代码如下
//
// LBHittestButton.m
// LBHitTestDemo
//
// Created by mac on 2024/6/2.
//
#import "LBHittestButton.h"
@interface LBHittestButton ()
@property (nonatomic, strong) UIButton *subButton;
@end
@implementation LBHittestButton
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self addSubview:self.subButton];
}
return self;
}
- (void)subButtonClick
{
NSLog(@"点击了子视图");
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint point2 = [self convertPoint:point toView:self.subButton];
if ([self.subButton pointInside:point2 withEvent:event]) {
return YES;
}
CGRect rect2 = UIEdgeInsetsInsetRect(self.bounds, self.expandEdgeInsets);
if (CGRectContainsPoint(rect2, point)) {
return YES;
}
return [super pointInside:point withEvent:event];
}
#pragma mark - lazy load
- (UIButton *)subButton
{
if (!_subButton) {
_subButton = [UIButton buttonWithType:UIButtonTypeCustom];
_subButton.frame = CGRectMake(100, 200, 40, 40);
_subButton.backgroundColor = [UIColor cyanColor];
[_subButton addTarget:self action:@selector(subButtonClick) forControlEvents:UIControlEventTouchUpInside];
}
return _subButton;
}
@end