Ios iPhone:检测自上次触摸屏幕以来的用户不活动/空闲时间

Ios iPhone:检测自上次触摸屏幕以来的用户不活动/空闲时间,ios,objective-c,iphone,idle-timer,Ios,Objective C,Iphone,Idle Timer,是否有人实现了一个功能,如果用户在某个时间段内没有触摸屏幕,您会采取某个操作?我在想最好的办法 UIC应用程序中有一种有点相关的方法: [UIApplication sharedApplication].idleTimerDisabled; 如果你有这样的东西就好了: NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed; 然后我可以设置一个计时器,定期检查这个值,当它超过阈值时采取一些措

是否有人实现了一个功能,如果用户在某个时间段内没有触摸屏幕,您会采取某个操作?我在想最好的办法

UIC应用程序中有一种有点相关的方法:

[UIApplication sharedApplication].idleTimerDisabled;
如果你有这样的东西就好了:

NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;
然后我可以设置一个计时器,定期检查这个值,当它超过阈值时采取一些措施


希望这能解释我在找什么。有没有人已经解决了这个问题,或者有没有想过你会怎么做?谢谢。

< P>你最终需要定义你认为闲置的是闲置的,如果没有计算资源,用户不接触屏幕的结果还是系统的状态?在许多应用中,即使用户没有通过触摸屏主动与设备交互,用户也有可能正在做一些事情。虽然用户可能熟悉设备进入睡眠状态的概念,并注意到它将通过屏幕变暗发生,但如果他们空闲,他们不一定会期望发生一些事情-您需要小心您将要做的事情。但是回到最初的陈述——如果你认为这第一个案例是你的定义,那么就没有一个简单的方法来做到这一点。您需要接收每个触摸事件,根据需要在响应器链上传递它,同时记录接收时间。这将为您进行空闲计算提供一些基础。如果你认为第二种情况是你的定义,那么你可以玩NSPStWORD空闲通知来尝试执行你当时的逻辑。

< P>这是我一直在寻找的答案:

让您的应用程序委托子类UIApplication。在实现文件中,重写sendEvent:方法,如下所示:

- (void)sendEvent:(UIEvent *)event {
    [super sendEvent:event];

    // Only want to reset the timer on a Began touch or an Ended touch, to reduce the number of timer resets.
    NSSet *allTouches = [event allTouches];
    if ([allTouches count] > 0) {
        // allTouches count only ever seems to be 1, so anyObject works here.
        UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
        if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded)
            [self resetIdleTimer];
    }
}

- (void)resetIdleTimer {
    if (idleTimer) {
        [idleTimer invalidate];
        [idleTimer release];
    }

    idleTimer = [[NSTimer scheduledTimerWithTimeInterval:maxIdleTime target:self selector:@selector(idleTimerExceeded) userInfo:nil repeats:NO] retain];
}

- (void)idleTimerExceeded {
    NSLog(@"idle time exceeded");
}
其中maxIdleTime和idleTimer是实例变量

为了使其正常工作,您还需要修改main.m,以告知UIApplicationMain使用您的委托类(在本例中为AppDelegate)作为主体类:

int retVal = UIApplicationMain(argc, argv, @"AppDelegate", @"AppDelegate");

这个线程是一个很大的帮助,我将它包装成一个UIWindow子类,用于发送通知。我选择通知使其成为真正的松耦合,但您可以很容易地添加委托

要点如下:


此外,UIApplication子类问题的原因是NIB被设置为创建2个UIApplication对象,因为它包含应用程序和委托。UIWindow子类工作得很好。

实际上,子类化思想工作得很好。只是不要让您的委托成为
UIApplication
子类。创建另一个从
UIApplication
继承的文件(例如myApp)。在IB中,将
fileOwner
对象的类设置为
myApp
,在myApp.m中实现上述
sendEvent
方法。总的来说,我要:

int retVal = UIApplicationMain(argc,argv,@"myApp.m",@"myApp.m")

我有一个空闲计时器解决方案的变体,它不需要对UIApplication进行子类化。它适用于特定的UIViewController子类,因此,如果您只有一个视图控制器(如交互式应用程序或游戏),或者只想处理特定视图控制器中的空闲超时,那么它非常有用

它也不会在每次重置空闲计时器时重新创建NSTimer对象。只有当计时器启动时,它才会创建一个新的

您的代码可以调用
resetIdleTimer
,以处理可能需要使空闲计时器无效的任何其他事件(例如重要的加速计输入)

@interface MainViewController:UIViewController
{
n定时器*空闲定时器;
}
@结束
#定义kMaxIdleTimeSeconds 60.0
@实现主视图控制器
#布拉格标记-
#pragma标记处理空闲超时
-(无效)重置IDletimer{
if(!idleTimer){
idleTimer=[[NSTimer scheduledTimerWithTimeInterval:kMaxIdleTimeSeconds
目标:自我
选择器:@selector(已超出idletimer)
用户信息:无
重复:否]保留];
}
否则{
if(晶圆厂([idleTimer.fireDate timeIntervalSinceNow])

(为了简洁起见,不包括内存清理代码。)

我刚刚遇到了一个由运动控制的游戏的问题,即禁用了屏幕锁定,但在菜单模式下应该再次启用。我没有使用计时器,而是将所有对
setIdleTimerDisabled
的调用封装在一个小类中,提供以下方法:

- (void) enableIdleTimerDelayed {
    [self performSelector:@selector (enableIdleTimer) withObject:nil afterDelay:60];
}

- (void) enableIdleTimer {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
}

- (void) disableIdleTimer {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
}
禁用空闲定时器
禁用空闲定时器,
enableIdleTimerDelayed
当进入菜单或任何应在空闲计时器处于活动状态下运行的内容时,将从AppDelegate的
applicationWillResignActive
方法调用
enableIdleTimer
,以确保将所有更改正确重置为系统默认行为。

我写了一篇文章并提供了单例类IdleTimerManager的代码这里是另一种检测活动的方法:

计时器是在
UITrackingRunLoopMode
中添加的,因此它只能在
UITracking
活动时触发。它还有一个很好的优点,就是不会对所有触摸事件向您发送垃圾邮件,从而通知您在最近的
activity\u DETECT\u TIMER\u RESOLUTION
秒内是否有活动。我将选择器命名为
keepAlive
,因为它似乎是合适的
- (void) enableIdleTimerDelayed {
    [self performSelector:@selector (enableIdleTimer) withObject:nil afterDelay:60];
}

- (void) enableIdleTimer {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
}

- (void) disableIdleTimer {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
}
_touchesTimer = [NSTimer timerWithTimeInterval:ACTIVITY_DETECT_TIMER_RESOLUTION
                                        target:self
                                      selector:@selector(keepAlive)
                                      userInfo:nil
                                       repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:_touchesTimer forMode:UITrackingRunLoopMode];
extension NSNotification.Name {
   public static let TimeOutUserInteraction: NSNotification.Name = NSNotification.Name(rawValue: "TimeOutUserInteraction")
}


class InterractionUIApplication: UIApplication {

static let ApplicationDidTimoutNotification = "AppTimout"

// The timeout in seconds for when to fire the idle timer.
let timeoutInSeconds: TimeInterval = 15 * 60

var idleTimer: Timer?

// Listen for any touch. If the screen receives a touch, the timer is reset.
override func sendEvent(_ event: UIEvent) {
    super.sendEvent(event)

    if idleTimer != nil {
        self.resetIdleTimer()
    }

    if let touches = event.allTouches {
        for touch in touches {
            if touch.phase == UITouchPhase.began {
                self.resetIdleTimer()
            }
        }
    }
}

// Resent the timer because there was user interaction.
func resetIdleTimer() {
    if let idleTimer = idleTimer {
        idleTimer.invalidate()
    }

    idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds, target: self, selector: #selector(self.idleTimerExceeded), userInfo: nil, repeats: false)
}

// If the timer reaches the limit as defined in timeoutInSeconds, post this notification.
func idleTimerExceeded() {
    NotificationCenter.default.post(name:Notification.Name.TimeOutUserInteraction, object: nil)
   }
} 
CommandLine.unsafeArgv.withMemoryRebound(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc)) {argv in
_ = UIApplicationMain(CommandLine.argc, argv, NSStringFromClass(InterractionUIApplication.self), NSStringFromClass(AppDelegate.self))
}
NotificationCenter.default.addObserver(self, selector: #selector(someFuncitonName), name: Notification.Name.TimeOutUserInteraction, object: nil)
fileprivate var timer ... //timer logic here

@objc public class CatchAllGesture : UIGestureRecognizer {
    override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesBegan(touches, with: event)
    }
    override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        //reset your timer here
        state = .failed
        super.touchesEnded(touches, with: event)
    }
    override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesMoved(touches, with: event)
    }
}

@objc extension YOURAPPAppDelegate {

    func addGesture () {
        let aGesture = CatchAllGesture(target: nil, action: nil)
        aGesture.cancelsTouchesInView = false
        self.window.addGestureRecognizer(aGesture)
    }
}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
import UIKit

class MyWindow: UIWindow {

    override func sendEvent(_ event: UIEvent){
        super.sendEvent(event)
        NSLog("Application received an event. Do whatever you want")
    }
}
self.window = MyWindow(frame: UIScreen.main.bounds)