如何在UIBarButtonItem中检测长按事件并区分点击与长按操作?
这个需求我之前在项目里实现过好几次,其实核心思路就是绕开UIBarButtonItem本身的限制——因为它没有直接支持长按手势的接口,所以我们可以通过给它绑定自定义视图或者给它的底层视图加手势来解决。下面给你两种实用的方案,你可以根据自己的场景选:
方案一:自定义UIButton作为UIBarButtonItem的customView(推荐)
这种方法最灵活,完全可控,而且能完美区分点击和长按事件。我们先创建一个UIButton,给它添加点击事件和长按手势,再把这个按钮包装成UIBarButtonItem即可:
override func viewDidLoad() { super.viewDidLoad() // 1. 创建自定义按钮,这里用system类型来匹配系统导航栏样式 let helpButton = UIButton(type: .system) helpButton.setTitle("帮助", for: .normal) // 匹配系统导航栏按钮的字体和颜色 helpButton.tintColor = navigationController?.navigationBar.tintColor helpButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular) // 2. 添加点击事件:跳转页面 helpButton.addTarget(self, action: #selector(navigateToTargetVC), for: .touchUpInside) // 3. 添加长按手势:显示帮助信息 let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(showHelpInfo(_:))) longPressGesture.minimumPressDuration = 0.5 // 可调整长按触发的最短时间,默认0.5秒 longPressGesture.delaysTouchesBegan = true // 优先判断长按,避免和点击冲突 helpButton.addGestureRecognizer(longPressGesture) // 4. 把按钮包装成UIBarButtonItem,添加到导航栏 let helpBarItem = UIBarButtonItem(customView: helpButton) navigationItem.rightBarButtonItem = helpBarItem } // 点击事件:跳转至目标ViewController @objc private func navigateToTargetVC() { let targetVC = YourTargetViewController() navigationController?.pushViewController(targetVC, animated: true) } // 长按事件:显示帮助信息(这里用UIAlertController举例) @objc private func showHelpInfo(_ gesture: UILongPressGestureRecognizer) { // 只在手势开始时触发一次,避免重复调用 guard gesture.state == .began else { return } let helpAlert = UIAlertController( title: "功能说明", message: "点击这个按钮可以跳转到设置页面;长按可以查看当前功能的使用指南", preferredStyle: .alert ) helpAlert.addAction(UIAlertAction(title: "知道了", style: .default)) present(helpAlert, animated: true) }
如果你用Objective-C,写法类似,核心逻辑一样:
- (void)viewDidLoad { [super viewDidLoad]; UIButton *helpButton = [UIButton buttonWithType:UIButtonTypeSystem]; [helpButton setTitle:@"帮助" forState:UIControlStateNormal]; helpButton.tintColor = self.navigationController.navigationBar.tintColor; helpButton.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightRegular]; [helpButton addTarget:self action:@selector(navigateToTargetVC) forControlEvents:UIControlEventTouchUpInside]; UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(showHelpInfo:)]; longPressGesture.minimumPressDuration = 0.5; longPressGesture.delaysTouchesBegan = YES; [helpButton addGestureRecognizer:longPressGesture]; UIBarButtonItem *helpBarItem = [[UIBarButtonItem alloc] initWithCustomView:helpButton]; self.navigationItem.rightBarButtonItem = helpBarItem; } - (void)navigateToTargetVC { YourTargetViewController *targetVC = [[YourTargetViewController alloc] init]; [self.navigationController pushViewController:targetVC animated:YES]; } - (void)showHelpInfo:(UILongPressGestureRecognizer *)gesture { if (gesture.state != UIGestureRecognizerStateBegan) return; UIAlertController *helpAlert = [UIAlertController alertControllerWithTitle:@"功能说明" message:@"点击这个按钮可以跳转到设置页面;长按可以查看当前功能的使用指南" preferredStyle:UIAlertControllerStyleAlert]; [helpAlert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil]]; [self presentViewController:helpAlert animated:YES completion:nil]; }
方案二:直接给UIBarButtonItem的底层视图添加手势(适合系统默认样式的按钮)
如果你不想自定义按钮,只想给系统自带的UIBarButtonItem(比如UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(editAction)))添加长按事件,可以通过获取它的底层视图来添加手势:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // 获取UIBarButtonItem对应的底层视图(注意:这里用到了私有属性"view",虽然目前苹果不会拒审,但后续系统更新可能失效) guard let barButtonView = navigationItem.rightBarButtonItem?.value(forKey: "view") as? UIView else { return } // 添加长按手势 let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(showHelpInfo(_:))) longPressGesture.minimumPressDuration = 0.5 barButtonView.addGestureRecognizer(longPressGesture) } // 长按事件处理和方案一一样 @objc private func showHelpInfo(_ gesture: UILongPressGestureRecognizer) { guard gesture.state == .began else { return } let helpAlert = UIAlertController(title: "功能说明", message: "点击进入编辑模式;长按查看编辑操作指南", preferredStyle: .alert) helpAlert.addAction(UIAlertAction(title: "知道了", style: .default)) present(helpAlert, animated: true) } // 系统按钮的点击事件原本就存在,不需要额外处理 @objc private func editAction() { // 你的编辑逻辑 }
一些注意事项
- 手势冲突处理:设置
longPressGesture.delaysTouchesBegan = true可以让系统先判断是否是长按,再触发点击事件,避免两种事件同时触发。 - 样式一致性:如果用自定义按钮,一定要匹配系统导航栏的
tintColor和字体,这样看起来不会违和。 - 方案二的风险:通过
value(forKey: "view")获取底层视图依赖于系统私有属性,虽然现在没问题,但苹果后续可能修改这个实现,所以优先推荐方案一。
内容的提问来源于stack exchange,提问作者Shivam Parmar
相关产品推荐
相关产品推荐

