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

Swift 4基于AppDelegate检测全控件用户无活动实现屏保

解决iOS全应用触摸检测与用户无活动状态识别问题

我明白你的痛点——原来的代码只能检测空白区域的触摸,UIButton、UITextField这些控件上的操作完全没被捕获,导致无活动状态的判断不准确。这其实是因为触摸事件的传递机制:控件会优先处理触摸,不会把事件传到AppDelegate;而你原来的TimerUIApplication的逻辑也有疏漏,没覆盖所有用户交互场景。

下面给你一套完整的解决方案,确保能捕获所有用户交互(包括控件触摸、键盘输入等):

一、核心原理:自定义UIApplication子类

UIApplication是所有事件的入口,不管是触摸、键盘还是手势,最终都会经过它的sendEvent(_:)方法。所以我们只需要重写这个方法,就能全局捕获用户活动。

二、修正后的TimerUIApplication代码

import UIKit

class TimerUIApplication: UIApplication {
    static let applicationDidTimeoutNotification = Notification.Name("AppTimeout")
    // 可根据需求调整超时时间
    private let timeoutInterval: TimeInterval = 5
    private var idleTimer: Timer?
    
    override init() {
        super.init()
        resetIdleTimer()
    }
    
    // 重置无活动计时器
    private func resetIdleTimer() {
        idleTimer?.invalidate()
        idleTimer = Timer.scheduledTimer(withTimeInterval: timeoutInterval, repeats: false) { [weak self] _ in
            self?.handleTimeout()
        }
    }
    
    // 处理超时逻辑
    private func handleTimeout() {
        NotificationCenter.default.post(name: Self.applicationDidTimeoutNotification, object: nil)
        print("用户已无活动")
        
        // 这里可以直接跳转到指定页面,或者让监听者处理
        DispatchQueue.main.async {
            let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
            guard let promoVC = mainStoryboard.instantiateViewController(withIdentifier: "mainPromo") as? UIViewController else { return }
            self.keyWindow?.rootViewController = promoVC
            self.keyWindow?.makeKeyAndVisible()
        }
    }
    
    override func sendEvent(_ event: UIEvent) {
        super.sendEvent(event)
        
        // 捕获所有触摸事件:只要有触摸开始/移动/结束,都算用户活动
        if let touches = event.allTouches {
            for touch in touches {
                if touch.phase != .cancelled {
                    resetIdleTimer()
                    break
                }
            }
        }
        
        // 额外捕获键盘事件(比如输入内容也算用户活动)
        if event.type == .keyboard {
            resetIdleTimer()
        }
    }
}

三、让应用使用自定义UIApplication子类

默认Xcode生成的项目用的是UIApplication,我们需要修改配置让系统使用TimerUIApplication:

方法1:修改Info.plist

  • 打开项目的Info.plist文件
  • 添加一个新的键:Principal class(或直接用英文键名NSPrincipalClass)
  • 设置值为你的类名,比如$(PRODUCT_MODULE_NAME).TimerUIApplication(确保模块名正确,一般就是你的项目名)

方法2:替换@UIApplicationMain

如果你原来的AppDelegate用了@UIApplicationMain,可以把它删掉,然后创建一个新的Swift文件(比如Main.swift),添加以下代码:

import UIKit

UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, NSStringFromClass(TimerUIApplication.self), NSStringFromClass(AppDelegate.self))

四、移除AppDelegate中的冗余代码

现在TimerUIApplication已经接管了计时器逻辑,你可以把AppDelegate里的idleTimer相关代码、touches系列方法都删掉,只保留必要的启动配置:

import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        application.isIdleTimerDisabled = true
        let userDefaults = UserDefaults.standard
        let defaultValues = ["promotionIsEnabled_preference" : "YES", "promotionDuration_preference" : "10"]
        userDefaults.register(defaults: defaultValues)
        return true
    }

    // 保留其他生命周期方法(applicationWillResignActive等),但移除计时器相关逻辑
}

五、监听超时通知(可选)

如果需要在某个ViewController里处理超时事件,可以注册通知:

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(handleAppTimeout), name: TimerUIApplication.applicationDidTimeoutNotification, object: nil)
}

@objc private func handleAppTimeout() {
    // 处理超时逻辑,比如弹出提示、清理数据等
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

为什么这个方案能捕获所有触摸?

  • sendEvent(_:)是UIApplication的核心方法,所有用户交互事件都会经过这里,包括控件上的触摸(因为控件的触摸事件也是通过UIApplication分发的)
  • 我们不仅处理了.began阶段的触摸,还覆盖了.moved、.ended,同时排除了.cancelled(比如触摸被系统中断的情况)
  • 额外添加了键盘事件的处理,确保用户输入文字也会重置计时器

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:35:09