traba problemas descargar apple iphone objective-c

iphone - problemas - ios 12



iPhone: detección de inactividad del usuario/tiempo de inactividad desde la última pantalla táctil (9)

Acabo de toparme con este problema con un juego que está controlado por movimientos, es decir, tiene el bloqueo de pantalla desactivado, pero debería habilitarlo de nuevo cuando esté en el modo de menú. En lugar de un temporizador, encapsulé todas las llamadas a setIdleTimerDisabled dentro de una clase pequeña que proporciona los siguientes métodos:

- (void) enableIdleTimerDelayed { [self performSelector:@selector (enableIdleTimer) withObject:nil afterDelay:60]; } - (void) enableIdleTimer { [NSObject cancelPreviousPerformRequestsWithTarget:self]; [[UIApplication sharedApplication] setIdleTimerDisabled:NO]; } - (void) disableIdleTimer { [NSObject cancelPreviousPerformRequestsWithTarget:self]; [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; }

disableIdleTimer desactiva el temporizador inactivo, enableIdleTimerDelayed al ingresar al menú o lo que deba ejecutarse con el temporizador inactivo activo y enableIdleTimer se llama desde el método applicationWillResignActive de AppDelegate para garantizar que todos sus cambios se restablecen correctamente al comportamiento predeterminado del sistema.
Escribí un artículo y proporcioné el código para la clase Singleton IdleTimerManager Idle Timer Handling en juegos de iPhone

¿Alguien ha implementado una función donde si el usuario no ha tocado la pantalla durante un cierto período de tiempo, realiza una determinada acción? Estoy tratando de encontrar la mejor manera de hacerlo.

Hay un método algo relacionado en UIApplication:

[UIApplication sharedApplication].idleTimerDisabled;

Sería bueno si en cambio tuvieras algo como esto:

NSTimeInterval timeElapsed = [UIApplication sharedApplication].idleTimeElapsed;

Entonces podría configurar un temporizador y verificar periódicamente este valor, y tomar alguna medida cuando exceda un umbral.

Espero que eso explique lo que estoy buscando. ¿Alguien ya ha abordado este tema o tiene alguna idea de 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 y idleTimer son variables de instancia.

Para que esto funcione, también necesita modificar su main.m para decirle a UIApplicationMain que use su clase de delegado (en este ejemplo, AppDelegate) como clase principal:

int retVal = UIApplicationMain(argc, argv, @"AppDelegate", @"AppDelegate");


Aquí hay otra forma de detectar actividad:

El temporizador se agrega en UITrackingRunLoopMode , por lo que solo puede UITracking si hay actividad de UITracking . También tiene la buena ventaja de no enviarle correos no deseados por todos los eventos táctiles, lo que le informa si hubo actividad en los últimos ACTIVITY_DETECT_TIMER_RESOLUTION segundos. Llamé al selector keepAlive ya que parece un caso de uso apropiado para esto. Por supuesto, puede hacer lo que desee con la información de que hubo actividad recientemente.

_touchesTimer = [NSTimer timerWithTimeInterval:ACTIVITY_DETECT_TIMER_RESOLUTION target:self selector:@selector(keepAlive) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:_touchesTimer forMode:UITrackingRunLoopMode];


En última instancia, debe definir lo que considera que está inactivo: ¿está inactivo el resultado de que el usuario no toque la pantalla o es el estado del sistema si no se utilizan recursos informáticos? Es posible, en muchas aplicaciones, que el usuario haga algo aunque no interactúe activamente con el dispositivo a través de la pantalla táctil. Si bien el usuario probablemente esté familiarizado con el concepto de que el dispositivo se va a dormir y el aviso de que sucederá a través del oscurecimiento de la pantalla, no es necesariamente el caso de que esperen que ocurra algo si están inactivos; debe tener cuidado. sobre lo que harías Pero volviendo a la afirmación original: si considera que el primer caso es su definición, no hay una manera realmente fácil de hacerlo. Necesitaría recibir cada evento táctil, pasarlo a lo largo de la cadena de respuesta según sea necesario, al tiempo de anotar la hora en que se recibió. Eso le dará alguna base para hacer el cálculo inactivo. Si considera que el segundo caso es su definición, puede jugar con una notificación NSPostWhenIdle para probar y ejecutar su lógica en ese momento.


En realidad, la idea de subclases funciona muy bien. Simplemente no haga que delegue la subclase UIApplication . Cree otro archivo que herede de UIApplication (por ejemplo, myApp). En IB, establezca la clase del objeto fileOwner en myApp y en myApp.m implemente el método sendEvent como se sendEvent anteriormente. En main.m do:

int retVal = UIApplicationMain(argc,argv,@"myApp.m",@"myApp.m")

et voilà!


Este hilo fue de gran ayuda, y lo envolví en una subclase UIWindow que envía notificaciones. Elegí las notificaciones para que sea un acoplamiento realmente flexible, pero puede agregar un delegado con la suficiente facilidad.

Aquí está la esencia:

http://gist.github.com/365998

Además, la razón del problema de la subclase UIApplication es que el NIB está configurado para crear 2 objetos UIApplication ya que contiene la aplicación y el delegado. La subclase UIWindow funciona muy bien.


Para swift 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) } }

crea el archivo main.swif y agrega 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)) }

Observando la notificación en cualquier otra clase

NotificationCenter.default.addObserver(self, selector: #selector(someFuncitonName), name: Notification.Name.TimeOutUserInteraction, object: nil)


Tengo una variación de la solución del temporizador inactivo que no requiere subclases UIApplication. Funciona en una subclase específica UIViewController, por lo que es útil si solo tiene un controlador de vista (como una aplicación interactiva o un juego puede tener) o solo desea manejar el tiempo de espera inactivo en un controlador de vista específico.

Tampoco vuelve a crear el objeto NSTimer cada vez que se restablece el temporizador de inactividad. Solo crea uno nuevo si el temporizador se dispara.

Su código puede llamar a resetIdleTimer para cualquier otro evento que pueda necesitar invalidar el temporizador inactivo (como la entrada de un acelerómetro significativo).

@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 brevedad).


Todo esto parece demasiado complejo. Creo que esta es una forma más limpia de usar todos los toques que necesita.

fileprivate var timer ... //timer logic here @objc public class CatchAllGesture : UIGestureRecognizer { override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) { super.touchesBegan(touches, with: event) } override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) { //reset your timer here state = .failed super.touchesEnded(touches, with: event) } override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) { super.touchesMoved(touches, with: event) } } @objc extension YOURAPPAppDelegate { func addGesture () { let aGesture = CatchAllGesture(target: nil, action: nil) aGesture.cancelsTouchesInView = false self.window.addGestureRecognizer(aGesture) } }

En su aplicación, el delegado finalizó el método de lanzamiento, simplemente llame a addGesture y ya está todo listo. Todos los toques se realizarán mediante los métodos de CatchAllGesture sin que esto impida la funcionalidad de los demás.