You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS如何检测手掌触摸/离开iPhone屏幕 实现求救呼叫按钮功能

解决方案

方案1:自定义高优先级长按时手势识别器(推荐,无私有API,可上架)

这是适配性最高的落地方案,实现逻辑如下:

  • 首先创建自定义UILongPressGestureRecognizer实例,配置基础参数:
// OC示例,Swift逻辑完全一致
UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
gesture.minimumPressDuration = 0; // 触摸立刻触发识别
gesture.allowableMovement = CGFLOAT_MAX; // 允许任意位置移动不中断识别
gesture.cancelsTouchesInView = NO;
gesture.delaysTouchesBegan = NO;
gesture.delaysTouchesEnded = NO;
gesture.delegate = self;
[self.view addGestureRecognizer:gesture];
  • 实现手势代理方法,提升手势优先级避免被系统拦截:
// 允许和所有其他手势同时识别
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}
// 接收所有类型的触摸事件,包含大面积手掌触摸
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    return YES;
}
// 始终允许自定义手势触发
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    return YES;
}
  • 处理手势状态,匹配业务逻辑:
- (void)handleGesture:(UILongPressGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateBegan) {
        // 首次检测到触摸,对应「屏幕被触摸」事件
    } else if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateCancelled) {
        // 所有触摸释放/手势结束,对应「所有触摸均被释放」事件
        // 额外判断触摸点数量避免误触发
        if (gesture.numberOfTouches == 0) {
            [self sendSOSSignal];
        }
    }
}
  • 额外配置屏蔽系统手势抢占:
    遍历当前window的所有手势,设置自定义手势优先级最高:
for (UIGestureRecognizer *sysGesture in self.view.window.gestureRecognizers) {
    [sysGesture requireGestureRecognizerToFail:gesture];
}

同时配置当前ViewController属性,屏蔽系统级交互干扰:

self.modalInPresentation = YES;
self.prefersStatusBarHidden = YES;
self.view.multipleTouchEnabled = YES;
self.view.userInteractionEnabled = YES;

方案2:重写UIWindow的sendEvent方法(补充兜底)

之前重写sendEvent无效是因为没有全量过滤事件类型,正确实现逻辑:

  • 子类化UIWindow,重写sendEvent:方法:
- (void)sendEvent:(UIEvent *)event {
    [super sendEvent:event];
    if (event.type == UIEventTypeTouches) {
        NSSet *allTouches = [event allTouches];
        NSInteger activeTouchCount = 0;
        for (UITouch *touch in allTouches) {
            if (touch.phase != UITouchPhaseCancelled && touch.phase != UITouchPhaseEnded) {
                activeTouchCount++;
            }
        }
        // 全局维护触摸计数,不需要走responder chain即可获取当前活跃触摸数
        [GlobalTouchManager sharedInstance].activeTouchCount = activeTouchCount;
        if (activeTouchCount == 0 && [GlobalTouchManager sharedInstance].hadTouch) {
            // 所有触摸释放,触发求救逻辑
            [self sendSOSSignal];
            [GlobalTouchManager sharedInstance].hadTouch = NO;
        } else if (activeTouchCount > 0) {
            [GlobalTouchManager sharedInstance].hadTouch = YES;
        }
    }
}

该方案可以拿到所有系统分发的触摸事件,包括被默认responder chain丢弃的大面积触摸事件,只要系统生成了触摸事件都会走到该方法中。

常见问题解答

  • 不通过responder chain直接查询屏幕触摸状态:没有公开API支持,私有APIGSEventGetSnapshot()可以获取全局触摸状态,但会被App Store审核拒绝,不建议使用。
  • 禁止touchesCancelled触发:按照方案1配置屏蔽所有系统手势抢占后,99%的非用户主动取消的touchesCancelled事件都会被消除,剩余极端场景(如触摸时APP退后台、来电弹窗)本身属于需要中断求救流程的场景,无需额外处理。

内容的提问来源于stack exchange,提问作者xpsysdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 17:24:01