iOS闲置一段时间后执行操作(无用户交互)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS闲置一段时间后执行操作(无用户交互)相关的知识,希望对你有一定的参考价值。

如何根据用户互动(或缺少互动)向我的ios应用添加计时器?换句话说,如果2分钟内没有用户交互,我想让该应用程序执行某些操作,在这种情况下,请导航至初始视图控制器。如果在1:55有人触摸屏幕,计时器将重置。我认为这将是一个全局计时器,因此无论您在哪个视图上,缺少交互都会启动计时器。虽然,我可以在每个视图上创建一个唯一的计时器。有人在此之前有任何建议,链接或示例代码吗?

答案

Anne提供的链接是一个很好的起点,但是,由于我是n00b,因此很难将其转化为我现有的项目。我发现一个博客(不再存在原始博客)可以提供更好的逐步指导,但是它不是为XCode 4.2和使用情节提要而编写的。这是我如何使不活动计时器为我的应用工作的文章:

  1. 创建新文件-> Objective-C类->输入名称(在我的情况下为TIMERUIApplication),并将子类更改为UIApplication。您可能必须在子类字段中手动键入此内容。您现在应该拥有适当的.h和.m文件。

  2. 将.h文件更改为如下:

    #import <Foundation/Foundation.h>
    
    //the length of time before your application "times out". This number actually represents seconds, so we'll have to multiple it by 60 in the .m file
    #define kApplicationTimeoutInMinutes 5
    
    //the notification your AppDelegate needs to watch for in order to know that it has indeed "timed out"
    #define kApplicationDidTimeoutNotification @"AppTimeOut"
    
    @interface TIMERUIApplication : UIApplication
    {
        NSTimer     *myidleTimer;
    }
    
    -(void)resetIdleTimer;
    
    @end
    
  3. 将.m文件更改为如下:

    #import "TIMERUIApplication.h"
    
    @implementation TIMERUIApplication
    
    //here we are listening for any touch. If the screen receives touch, the timer is reset
    -(void)sendEvent:(UIEvent *)event
    {
        [super sendEvent:event];
    
        if (!myidleTimer)
        {
            [self resetIdleTimer];
        }
    
        NSSet *allTouches = [event allTouches];
        if ([allTouches count] > 0)
        {
            UITouchPhase phase = ((UITouch *)[allTouches anyObject]).phase;
            if (phase == UITouchPhaseBegan || phase == UITouchPhaseMoved)
            {
                [self resetIdleTimer];
            }
    
        }
    }
    //as labeled...reset the timer
    -(void)resetIdleTimer
    {
        if (myidleTimer)
        {
            [myidleTimer invalidate];
        }
        //convert the wait period into minutes rather than seconds
        int timeout = kApplicationTimeoutInMinutes * 60;
        myidleTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:@selector(idleTimerExceeded) userInfo:nil repeats:NO];
    
    }
    //if the timer reaches the limit as defined in kApplicationTimeoutInMinutes, post this notification
    -(void)idleTimerExceeded
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:kApplicationDidTimeoutNotification object:nil];
    }
    
    
    @end
    
  4. 进入您的Supporting Files文件夹,将main.m更改为此(与以前的XCode版本不同:

    #import <UIKit/UIKit.h>
    
    #import "AppDelegate.h"
    #import "TIMERUIApplication.h"
    
    int main(int argc, char *argv[])
    {
        @autoreleasepool {
            return UIApplicationMain(argc, argv, NSStringFromClass([TIMERUIApplication class]), NSStringFromClass([AppDelegate class]));
        }
    }
    
  5. 将其余代码写入AppDelegate.m文件中。我遗漏了与该过程无关的代码。 .h文件中没有任何更改。

    #import "AppDelegate.h"
    #import "TIMERUIApplication.h"
    
    @implementation AppDelegate
    
    @synthesize window = _window;
    
    -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    {      
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidTimeout:) name:kApplicationDidTimeoutNotification object:nil];
    
        return YES;
    }
    
    -(void)applicationDidTimeout:(NSNotification *) notif
    {
        NSLog (@"time exceeded!!");
    
    //This is where storyboarding vs xib files comes in. Whichever view controller you want to revert back to, on your storyboard, make sure it is given the identifier that matches the following code. In my case, "mainView". My storyboard file is called MainStoryboard.storyboard, so make sure your file name matches the storyboardWithName property.
        UIViewController *controller = [[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:@"mainView"];
    
        [(UINavigationController *)self.window.rootViewController pushViewController:controller animated:YES];
    }
    

注意:只要检测到触摸,计时器就会启动。这意味着,即使用户在没有离开该视图的情况下触摸主屏幕(在我的情况下为“ mainView”),相同的视图也会在分配的时间后移开。对于我的应用程序而言,这没什么大不了的,但是对于您的应用程序而言,可能并不重要。仅在识别到触摸后计时器才会重置。如果想在返回到想要的页面后立即重置计时器,请在... pushViewController:controller animation:YES];

之后添加此代码。
[(TIMERUIApplication *)[UIApplication sharedApplication] resetIdleTimer];

如果视图只是坐在那里而没有交互,则将导致视图每x分钟推送一次。计时器每次识别到触摸都会重置,因此仍然可以使用。

如果您提出了改进建议,请发表评论,尤其是如果当前正在显示“ mainView”,则以某种方式禁用计时器。我似乎无法弄清楚我的if语句是否可以让它注册当前视图。但是我对自己的位置感到满意。下面是我对if语句的初步尝试,因此您可以了解我的处理方法。

-(void)applicationDidTimeout:(NSNotification *) notif
{
    NSLog (@"time exceeded!!");
    UIViewController *controller = [[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:@"mainView"];

    //I've tried a few varieties of the if statement to no avail. Always goes to else.
    if ([controller isViewLoaded]) {
        NSLog(@"Already there!");
    }
    else {
        NSLog(@"go home");
        [(UINavigationController *)self.window.rootViewController pushViewController:controller animated:YES];
        //[(TIMERUIApplication *)[UIApplication sharedApplication] resetIdleTimer];
    }
}

我仍然是一名n00b,可能并没有以最好的方式做所有事情。总是欢迎提出建议。

另一答案

我已经实现了Bobby的建议,但是在Swift中。该代码概述如下。

  1. 创建新文件-> Swift文件->输入名称(以我为例TimerUIApplication),并将子类更改为UIApplication。更改TimerUIApplication.swift文件的内容如下:

    class TimerUIApplication: UIApplication {
    
        static let ApplicationDidTimoutNotification = "AppTimout"
    
        // The timeout in seconds for when to fire the idle timer.
        let timeoutInSeconds: TimeInterval = 5 * 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 event.allTouches?.contains(where: { $0.phase == .began || $0.phase == .moved }) == true {
                resetIdleTimer()
            }
        }
    
        // Resent the timer because there was user interaction.
        func resetIdleTimer() {
            idleTimer?.invalidate()
            idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds, target: self, selector: #selector(AppDelegate.idleTimerExceeded), userInfo: nil, repeats: false)
        }
    
        // If the timer reaches the limit as defined in timeoutInSeconds, post this notification.
        func idleTimerExceeded() {
            Foundation.NotificationCenter.default.post(name: NSNotification.Name(rawValue: TimerUIApplication.ApplicationDidTimoutNotification), object: nil)
        }
    }
    
  2. 创建新文件-> Swift文件-> main.swift(名称为重要)。

    import UIKit
    
    UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(TimerUIApplication), NSStringFromClass(AppDelegate))
    
  3. 在您的AppDelegate中:@UIApplicationMain移到AppDelegate。

  4. class AppDelegate: UIResponder, UIApplicationDelegate {
    
        func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
            NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AppDelegate.applicationDidTimout(_:)), name: TimerUIApplication.ApplicationDidTimoutNotification, object: nil)
            return true
        }
    
        ...
    
        // The callback for when the timeout was fired.
        func applicationDidTimout(notification: NSNotification) {
            if let vc = self.window?.rootViewController as? UINavigationController {
                if let myTableViewController = vc.visibleViewController as? MyMainViewController {
                    // Call a function defined in your view controller.
                    myMainViewController.userIdle()
                } else {
                  // We are not on the main view controller. Here, you could segue to the desired class.
                  let storyboard = UIStoryboard(name: "MyStoryboard", bundle: nil)
                  let vc = storyboard.instantiateViewControllerWithIdentifier("myStoryboardIdentifier")
                }
            }
        }
    }
    

    请记住,根据您的根视图控制器,您可能必须在applicationDidTimout中执行不同的操作。有关应如何投射视图控制器的更多详细信息,请参见this post。如果您在导航控制器上有模式视图,则可能要使用以上是关于iOS闲置一段时间后执行操作(无用户交互)的主要内容,如果未能解决你的问题,请参考以下文章

    防止 iOS 移动 Safari 闲置/自动锁定/休眠?

    ios12后台定位服务停止

    Linux提升系统安全性:自动注销 TMOUT

    Python网络编程

    如何使用 xcode 将快照划分为多个片段,以便让用户与每个片段进行交互?

    Python异步IO