iPhone: Detectando inactividad/tiempo de inactividad del usuario desde el último toque de pantalla
¿Alguien ha implementado una función en la que, si el usuario no ha tocado la pantalla durante un período de tiempo determinado, realiza una determinada acción? Estoy tratando de encontrar la mejor manera de hacerlo.
Existe este método algo relacionado en UIApplication:
[UIApplication sharedApplication].idleTimerDisabled;
Sería bueno si en su lugar tuvieras algo como esto:
NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;
Luego podría configurar un temporizador y verificar periódicamente este valor y tomar alguna medida cuando supere un umbral.
Ojalá eso explique lo que estoy buscando. ¿Alguien ha abordado ya este problema o tiene alguna idea sobre cómo lo haría? Gracias.
Aquí está la respuesta que había estado buscando:
Haga que su aplicación delegue la subclase UIApplication. En el archivo de implementación, anule el método sendEvent: así:
- (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");
}
donde maxIdleTime e idleTimer son variables de instancia.
Para que esto funcione, también necesita modificar su archivo main.m para indicarle a UIApplicationMain que use su clase delegada (en este ejemplo, AppDelegate) como clase principal:
int retVal = UIApplicationMain(argc, argv, @"AppDelegate", @"AppDelegate");
Tengo una variación de la solución del temporizador inactivo que no requiere subclasificar UIApplication. Funciona en una subclase UIViewController específica, por lo que es útil si solo tiene un controlador de vista (como puede tener una aplicación o juego interactivo) o solo desea manejar el tiempo de espera de inactividad en un controlador de vista específico.
Tampoco vuelve a crear el objeto NSTimer cada vez que se reinicia el temporizador de inactividad. Sólo crea uno nuevo si se activa el temporizador.
Su código puede solicitar resetIdleTimer
cualquier otro evento que pueda necesitar invalidar el temporizador inactivo (como una entrada significativa del acelerómetro).
@interface MainViewController : UIViewController
{
NSTimer *idleTimer;
}
@end
#define kMaxIdleTimeSeconds 60.0
@implementation MainViewController
#pragma mark -
#pragma mark Handling idle timeout
- (void)resetIdleTimer {
if (!idleTimer) {
idleTimer = [[NSTimer scheduledTimerWithTimeInterval:kMaxIdleTimeSeconds
target:self
selector:@selector(idleTimerExceeded)
userInfo:nil
repeats:NO] retain];
}
else {
if (fabs([idleTimer.fireDate timeIntervalSinceNow]) < kMaxIdleTimeSeconds-1.0) {
[idleTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:kMaxIdleTimeSeconds]];
}
}
}
- (void)idleTimerExceeded {
[idleTimer release]; idleTimer = nil;
[self startScreenSaverOrSomethingInteresting];
[self resetIdleTimer];
}
- (UIResponder *)nextResponder {
[self resetIdleTimer];
return [super nextResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self resetIdleTimer];
}
@end
(Código de limpieza de memoria excluido por razones de brevedad).
Para rápido v 3.1
No olvides comentar esta línea en AppDelegate //@UIApplicationMain
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)
}
}
cree el archivo main.swif y agregue esto (el nombre es importante)
CommandLine.unsafeArgv.withMemoryRebound(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc)) {argv in
_ = UIApplicationMain(CommandLine.argc, argv, NSStringFromClass(InterractionUIApplication.self), NSStringFromClass(AppDelegate.self))
}
Observar la notificación en cualquier otra clase.
NotificationCenter.default.addObserver(self, selector: #selector(someFuncitonName), name: Notification.Name.TimeOutUserInteraction, object: nil)