views:

4274

answers:

5

Hi all,

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.

+1  A: 

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.

wisequark
Just to clarify, I am talking about interaction with the screen. I'll update the question to reflect that.
Mike McMaster
Then you can implement something where any time a touch happens you update a value which you check, or even set (and reset) an idle timer to fire, but you need to implement it yourself, because as wisequark said, what constitutes idle varies between different apps.
Louis Gerbarg
I am defining "idle" strictly as time since last touching the screen. I get that I'll need to implement it myself, I was just wondering what the "best" way would be to, say, intercept screen touches, or if someone knows of an alternative method to determine this.
Mike McMaster
+5  A: 

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:

- (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");
}

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:

int retVal = UIApplicationMain(argc, argv, @"AppDelegate", @"AppDelegate");
Mike McMaster
Hi Mike, My AppDelegate is inherting from NSObject So changed it UIApplication and Implement above methods to detect user becoming idle but i am getting error "Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'There can only be one UIApplication instance.'".. is anything else i need to do...?
mihirpmehta
A: 

Hi Mike,

This exactly what i'm looking for. However my app crashes after that changes in the main.m with:

     2009-03-18 15:09:37.436 MyApp[195:20b] *** Assertion failure in -[AppDelegate init], /SourceCache/UIKit/UIKit-747.38/UIApplication.m:520
2009-03-18 15:09:37.454 MyApp[195:20b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'There can only be one UIApplication instance.'

and the Stack.

Markus Scheucher
Hmm. My first thought is maybe you're calling UIApplicationMain() more than once? Not sure what else the problem might be, to be honest. I never had this problem.
Mike McMaster
No, Not knowingly.There is just one UIApplicationMain() in the main.m. This problem occurs when i subclass the UIApplication.Have you tried this solution with any of the newer SDKs ? (I use 2.2.1)
Markus Scheucher
I#ve found anothe way. I've subclassed UIWindow and set it in the MainWindow.xib. This class has a sendEvent, too. It works well with your code. thanks
Markus Scheucher
A: 

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.

Brian King
A: 

Actually the subclassing idea works great. Just don't make your delegate the UIApplication subclass. Create another file that inherit from UIApplication (e.g. myApp) in IB set the class of the fileOwner object to myApp, in myApp.m implement the sendEvent method as above, in main.m: int retVal = UIApplicationMain(argc,argv,@"myApp.m",@"myApp.m") at voila

Roby