iPhone:检测自上次屏幕触摸以来用户不活动/空闲时间
有没有人实现了一项功能,如果用户在一段时间内没有触摸屏幕,您会采取特定的操作? 我正在努力找出最好的方法来做到这一点。
UIApplication 中有一个与此有些相关的方法:
[UIApplication sharedApplication].idleTimerDisabled;
如果您有这样的方法那就太好了:
NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;
然后我可以设置一个计时器并定期检查该值,并在超过阈值时采取一些操作。
希望这能解释我在寻找什么。 有没有人已经解决了这个问题,或者对如何做到这一点有任何想法? 谢谢。
Has anybody implemented a feature where if the user has not touched the screen for a certain time period, you take a certain action? I'm trying to figure out the best way to do that.
There's this somewhat-related method in UIApplication:
[UIApplication sharedApplication].idleTimerDisabled;
It'd be nice if you instead had something like this:
NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;
Then I could set up a timer and periodically check this value, and take some action when it exceeds a threshold.
Hopefully that explains what I'm looking for. Has anyone tackled this issue already, or have any thoughts on how you would do it? Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
这是我一直在寻找的答案:
让您的应用程序委托子类 UIApplication。 在实现文件中,重写 sendEvent: 方法,如下所示:
其中 maxIdleTime 和idleTimer 是实例变量。
为了使其工作,您还需要修改 main.m 以告诉 UIApplicationMain 使用委托类(在本例中为 AppDelegate)作为主体类:
Here's the answer I had been looking for:
Have your application delegate subclass UIApplication. In the implementation file, override the sendEvent: method like so:
where maxIdleTime and idleTimer are instance variables.
In order for this to work, you also need to modify your main.m to tell UIApplicationMain to use your delegate class (in this example, AppDelegate) as the principal class:
我有一个空闲计时器解决方案的变体,它不需要子类化 UIApplication。 它适用于特定的 UIViewController 子类,因此如果您只有一个视图控制器(如交互式应用程序或游戏可能有)或只想处理特定视图控制器中的空闲超时,则非常有用。
每次重置空闲计时器时,它也不会重新创建 NSTimer 对象。 如果计时器触发,它只会创建一个新计时器。
您的代码可以针对可能需要使空闲计时器无效的任何其他事件(例如重要的加速度计输入)调用
resetIdleTimer
。(为简洁起见,排除了内存清理代码。)
I have a variation of the idle timer solution which doesn't require subclassing UIApplication. It works on a specific UIViewController subclass, so is useful if you only have one view controller (like an interactive app or game may have) or only want to handle idle timeout in a specific view controller.
It also does not re-create the NSTimer object every time the idle timer is reset. It only creates a new one if the timer fires.
Your code can call
resetIdleTimer
for any other events that may need to invalidate the idle timer (such as significant accelerometer input).(memory cleanup code excluded for brevity.)
对于 swift v 3.1,
不要忘记在 AppDelegate //@UIApplicationMain 中注释这一行
在任何其他类中观察通知
For swift v 3.1
dont't forget comment this line in AppDelegate //@UIApplicationMain
Observing notification in an any other class
该线程提供了很大的帮助,我将其包装到一个发送通知的 UIWindow 子类中。 我选择通知是为了使其成为真正的松散耦合,但您可以轻松添加委托。
要点如下:
http://gist.github.com/365998
另外,UIApplication 子类的原因问题是 NIB 设置为创建 2 个 UIApplication 对象,因为它包含应用程序和委托。 UIWindow 子类效果很好。
This thread was a great help, and I wrapped it up into a UIWindow subclass that sends out notifications. I chose notifications to make it a real loose coupling, but you can add a delegate easily enough.
Here's the gist:
http://gist.github.com/365998
Also, the reason for the UIApplication subclass issue is that the NIB is setup to then create 2 UIApplication objects since it contains the application and the delegate. UIWindow subclass works great though.
有一种方法可以在整个应用程序范围内执行此操作,而无需单个控制器执行任何操作。 只需添加一个不会取消触摸的手势识别器即可。 这样,计时器将跟踪所有触摸,并且其他触摸和手势根本不受影响,因此其他人不必知道这一点。
在您的应用程序委托的 did finish launch 方法中,只需调用 addGesture 即可完成设置。 所有触摸都将通过 CatchAllGesture 的方法,而不会妨碍其他功能。
There's a way to do this app wide without individual controllers having to do anything. Just add a gesture recognizer that doesn't cancel touches. This way, all touches will be tracked for the timer, and other touches and gestures aren't affected at all so no one else has to know about it.
In your app delegate's did finish launch method, just call addGesture and you're all set. All touches will go through the CatchAllGesture's methods without it preventing the functionality of others.
实际上,子类化的想法非常有效。 只是不要让您的委托成为
UIApplication
子类。 创建另一个继承自UIApplication
的文件(例如 myApp)。 在 IB 中,将fileOwner
对象的类设置为myApp
,并在 myApp.m 中实现上面的sendEvent
方法。 在 main.m 中执行:等瞧!
Actually the subclassing idea works great. Just don't make your delegate the
UIApplication
subclass. Create another file that inherits fromUIApplication
(e.g. myApp). In IB set the class of thefileOwner
object tomyApp
and in myApp.m implement thesendEvent
method as above. In main.m do:et voilà!
我刚刚在一个由动作控制的游戏中遇到了这个问题,即禁用了屏幕锁定,但在菜单模式下应该再次启用它。 我将所有对
setIdleTimerDisabled
的调用封装在一个小类中,而不是计时器,提供以下方法:disableIdleTimer
停用空闲计时器,enableIdleTimerDelayed
进入菜单时或者任何应该在空闲计时器处于活动状态时运行,并且从 AppDelegate 的applicationWillResignActive
方法调用enableIdleTimer
来确保所有更改都正确重置为系统默认行为。我写了一篇文章并提供了单例类 IdleTimerManager 的代码 iPhone 游戏中的空闲定时器处理
I just ran into this problem with a game that is controlled by motions i.e. has screen lock disabled but should enable it again when in menu mode. Instead of a timer I encapsulated all calls to
setIdleTimerDisabled
within a small class providing the following methods:disableIdleTimer
deactivates idle timer,enableIdleTimerDelayed
when entering the menu or whatever should run with idle timer active andenableIdleTimer
is called from your AppDelegate'sapplicationWillResignActive
method to ensure all your changes are reset properly to the system default behaviour.I wrote an article and provided the code for the singleton class IdleTimerManager Idle Timer Handling in iPhone Games
这是检测活动的另一种方法:
计时器添加在
UITrackingRunLoopMode
中,因此只有在存在UITracking
活动时才会触发。 它还具有一个很好的优点,即不会向您发送所有触摸事件的垃圾邮件,从而通知您在最后ACTIVITY_DETECT_TIMER_RESOLUTION
秒内是否有活动。 我将选择器命名为“keepAlive”,因为它似乎是一个合适的用例。 当然,您可以根据最近有活动的信息做任何您想做的事情。Here is another way to detect activity:
The timer is added in
UITrackingRunLoopMode
, so it can only fire if there isUITracking
activity. It also has the nice advantage of not spamming you for all touch events, thus informing if there was activity in the lastACTIVITY_DETECT_TIMER_RESOLUTION
seconds. I named the selectorkeepAlive
as it seems an appropriate use case for this. You can of course do whatever you desire with the information that there was activity recently.最终,您需要定义什么是空闲 - 空闲是用户没有触摸屏幕的结果,还是没有使用计算资源时的系统状态? 在许多应用中,即使用户没有通过触摸屏主动与设备交互,也有可能正在做某事。 虽然用户可能熟悉设备进入睡眠状态的概念,以及通过屏幕调暗来进入睡眠状态的通知,但他们不一定会期望在闲置时会发生某些事情 - 你需要小心关于你会做什么。 但回到最初的陈述 - 如果您认为第一种情况是您的定义,那么没有真正简单的方法可以做到这一点。 您需要接收每个触摸事件,根据需要将其传递到响应者链上,同时记下接收时间。 这将为您进行空闲计算提供一些基础。 如果您认为第二种情况是您的定义,您可以使用 NSPostWhenIdle 通知来尝试在当时执行您的逻辑。
Ultimately you need to define what you consider to be idle - is idle the result of the user not touching the screen or is it the state of the system if no computing resources are being used? It is possible, in many applications, for the user to be doing something even if not actively interacting with the device through the touch screen. While the user is probably familiar with the concept of the device going to sleep and the notice that it will happen via screen dimming, it is not necessarily the case that they'll expect something to happen if they are idle - you need to be careful about what you would do. But going back to the original statement - if you consider the 1st case to be your definition, there is no really easy way to do this. You'd need to receive each touch event, passing it along on the responder chain as needed while noting the time it was received. That will give you some basis for making the idle calculation. If you consider the second case to be your definition, you can play with an NSPostWhenIdle notification to try and perform your logic at that time.
外面是 2021 年,我想分享我在不扩展 UIApplication 的情况下处理这个问题的方法。 我不会描述如何创建计时器并重置它。 而是如何捕获所有事件。 所以你的 AppDelegate 是这样开始的:
所以你需要做的就是继承 UIWindow 并重写
sendEvent
,如下所示然后用我们的类创建窗口:
Outside is 2021 and I would like share my approach to handle this without extending the UIApplication. I will not describe how to create a timer and reset it. But rather how to catch all events. So your AppDelegate starts with this:
So all you need to do is to subclass UIWindow and override
sendEvent
, like belowAnd later create window with our class: