为UIBarButtonItem两侧添加UITapGestureRecognizer扩大点击区域
结论
该需求完全可以实现,有两种可行方案,根据你的实际场景选择即可。
方案1:直接扩大按钮热区(优先推荐,无需手势)
这是实现成本最低、无冲突的方案,不需要依赖你加在导航栏上的手势识别器:
- 直接把自定义
UIButton的尺寸拓宽,覆盖左右各25像素的响应范围 - 通过
imageEdgeInsets调整图标显示位置,保持视觉效果和你原来的设计完全一致 - 系统会自动将按钮的bounds作为点击响应区域,不需要额外写点击判断逻辑
对应修改后的createSearchButton代码:
- (UIButton *) createSearchButton { UIButton *searchButton = [UIButton buttonWithType:UIButtonTypeCustom]; [searchButton setImage:[UIImage sdk_imageName:@"Search_header_black"] forState:UIControlStateNormal]; [searchButton addTarget:self action:@selector(searchBarButtonClick) forControlEvents:UIControlEventTouchUpInside]; // 原宽度32,左右各扩展25像素,总宽度为32 + 25*2 = 82 searchButton.size = CGSizeMake(82, 30); // 调整图标偏移量,保持视觉位置和原设计一致(原left偏移8,加上左侧扩展的25,总偏移33) searchButton.imageEdgeInsets = UIEdgeInsetsMake(0, 33, 0, 0); return searchButton; }
方案2:通过现有导航栏手势识别器判断点击位置
如果你有特殊需求必须用手势实现(比如扩展区域的点击要执行和按钮点击不同的逻辑),可以在手势回调中判断点击坐标是否落在目标范围内,步骤如下:
- 将
searchButton设置为类的持有属性,不要仅作为局部变量使用,方便后续获取位置 - 给手势设置
cancelsTouchesInView = NO,避免手势拦截导航栏上其他控件(如返回按钮、其他barButtonItem)的原生点击事件 - 在手势回调中,将点击坐标和按钮位置都转换到导航栏的坐标系下,判断点击是否落在按钮左右各25像素的扩展范围内
对应代码实现:
// 首先在类的延展中添加属性,持有搜索按钮 @property (nonatomic, strong) UIButton *searchButton; // 初始化导航栏相关控件的部分代码 self.searchButton = [self createSearchButton]; UIBarButtonItem *searchBarItem = [[UIBarButtonItem alloc] initWithCustomView:self.searchButton]; // 按你的业务需求设置为left/rightBarButtonItem self.navigationItem.leftBarButtonItem = searchBarItem; UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGestureForTopLeftNotificationBarButtonItem:)]; // 不拦截导航栏上其他控件的触摸事件 gr.cancelsTouchesInView = NO; [self.navigationController.navigationBar addGestureRecognizer:gr];
// 手势回调实现 - (void)handleGestureForTopLeftNotificationBarButtonItem:(UITapGestureRecognizer *)gr { if (gr.state != UIGestureRecognizerStateRecognized) { return; } // 获取手势在导航栏上的点击坐标 CGPoint tapLocation = [gr locationInView:self.navigationController.navigationBar]; // 将搜索按钮的bounds转换为导航栏坐标系下的frame CGRect btnOriginalFrame = [self.searchButton convertRect:self.searchButton.bounds toView:self.navigationController.navigationBar]; // 生成左右各扩展25像素的热区 CGRect expandHotArea = CGRectInset(btnOriginalFrame, -25, 0); // 判断点击是否在扩展热区内,且不在按钮原有响应区域内(避免和按钮本身的点击事件重复触发) if (CGRectContainsPoint(expandHotArea, tapLocation) && !CGRectContainsPoint(btnOriginalFrame, tapLocation)) { // 执行对应逻辑 [self searchBarButtonClick]; } }
注意事项
- 坐标转换步骤不能省略:因为搜索按钮是作为
UIBarButtonItem的customView存在的,它的默认父视图不是navigationBar,直接取frame会出现坐标偏移,导致热区判断错误 - 如果使用手势方案,一定要设置
cancelsTouchesInView = NO,否则会导致导航栏上其他系统或自定义按钮无法响应点击
内容的提问来源于stack exchange,提问作者stefanosn
相关产品推荐
相关产品推荐

