iPhone: Detectando inactividad/tiempo de inactividad del usuario desde el último toque de pantalla

Resuelto Mike McMaster asked hace 54 años • 10 respuestas

¿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.

Mike McMaster avatar Jan 01 '70 08:01 Mike McMaster
Aceptado

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");
Mike McMaster avatar Nov 21 '2008 17:11 Mike McMaster

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 resetIdleTimercualquier 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).

Chris Miles avatar Mar 11 '2011 06:03 Chris Miles

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)
Sergey Stadnik avatar May 12 '2017 14:05 Sergey Stadnik