ios - para - cuenta regresiva dias app
temporizador de cuenta regresiva de fondo a prueba de tiempo nativo de iOS nativo de iOS (2)
Me gustaría una forma de crear un temporizador de cuenta atrás de fondo a prueba de tiempo nativo de iOS nativo , por ejemplo para contar tiempos para la próxima recompensa en los juegos.
Explicación del objeto:
local
- Funciona sin conexión a internet
iOS nativo
- Quiero encontrar una solución en (preferiblemente) Objective-C o Swift
prueba a prueba de tiempo
- Cuando un usuario cambia el tiempo de su dispositivo hacia adelante / hacia atrás, el tiempo restante se mantiene igual
fondo
- Apagar / volver a abrir amistoso
contador regresivo
- Un temporizador que, después del inicio, proporcionará un método para verificar el tiempo restante
Me gustaría tener un sistema de recompensa basado en el tiempo en mi juego para iOS, que no será fácilmente pirateable solo moviendo el dispositivo hacia delante.
Originalmente, pensé que esto no era posible y seguí usando [NSDate date]
y savedDate
, comparándolos para encontrar el tiempo transcurrido. Recientemente, sin embargo, me encontré con un juego que usa esta característica: Crossy Road . Ese es un juego de Unity, hasta donde yo sé, pero dudo que una característica algo común como esta disponible en el proyecto Unity que se compila en una aplicación iOS no esté accesible en iOS a través de Objective-C o Swift.
Proporcionan un sistema de recompensa cronometrada, una vez cada 6 horas, y, por lo que he probado, no es pirateable al cambiar el tiempo del dispositivo. También funciona sin conexión a Internet. (No estoy seguro si funciona si no te conectas, pero cuando me conecté, desconecté e intenté usar el hack, no funcionó).
Puede usar el tiempo de actividad actual del sistema ( mach_absolute_time()
). Se puede acceder fácilmente a esta función usando CACurrentMediaTime()
que la devuelve fácilmente en segundos. Entonces probablemente tenga que configurar algún tipo de intervalo para verificar este valor. Por supuesto, esto se puede hacer usando NSDate, ya que no es un problema que el usuario active el reloj. Cuando se trata de reiniciar, simplemente guarde el valor de inicio y utilícelo como un desplazamiento después del reinicio.
El único inconveniente es que no cuenta el tiempo que el dispositivo está apagado, pero en general no es un gran problema en estos días cuando nadie apaga sus teléfonos.
Para obtener la función del temporizador real, simplemente guarde el valor al principio y luego compruébelo regularmente con el valor final proyectado, quizás con resoluciones crecientes a medida que se acerca el final.
Tenía exactamente los mismos requisitos para una aplicación que estoy construyendo. Intenté usar CACurrentMediaTime () pero cuando apagas el dispositivo, se restablece a 0, que no es lo que quieres. Lo que funcionó incluso al apagar el dispositivo fue utilizar esta referencia de tiempo:
currentUser.timeLastUpdated = [[NSDate date] timeIntervalSince1970];
Aquí está el código para ayudarlo a implementar el temporizador. Esto se modifica desde otro subproceso de .
En MainViewController.m
@interface MainViewController ()
@property (strong, nonatomic) IBOutlet UILabel *countdownLabel;
@end
@implementation MainViewController
{
UserData *_currentUser;
int hours;
int minutes;
int seconds;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_currentUser = [UserData currentUser];
[self updateTimerText];
[self startTimer];
}
- (void)startTimer {
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES];
}
- (void)updateTimer:(NSTimer *)timer {
if (_currentUser.secondsLeft > 0) {
_currentUser.secondsLeft--;
[self updateTimerText];
}
}
- (void)updateTimerText {
int secondsLeft = _currentUser.secondsLeft;
hours = secondsLeft / 3600;
minutes = (secondsLeft%3600) / 60;
seconds = (secondsLeft%3600) % 60;
self.countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d",hours,minutes,seconds];
}
}
Ahora, para que sea a prueba de balas y no deje de contar cuando detenga la aplicación, debe modificar AppDelegate.m.
En AppDelegate.m
@implementation AppDelegate
{
UserData *currentUser;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Intialize currentUser
currentUser = [UserData currentUser];
// Reset secondsLeft based on lastTimeUpdated
if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) {
currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated;
}
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
// Set the last time updated (only need to call it here because this is coupled with applicationWillTerminate
currentUser.lastTimeUpdated = (int)[[NSDate date] timeIntervalSince1970];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
// Reset secondsLeft based on lastTimeUpdated
if (currentUser.secondsLeft > 0 && currentUser.lastTimeUpdated != 0) {
currentUser.secondsLeft -= ((int)[[NSDate date] timeIntervalSince1970]) - currentUser.lastTimeUpdated;
}
}
Solo necesita modificar tres de los métodos en AppDelegate porque ApplicationWillTerminate siempre se activa con ApplicationDidEnterBackground (hasta donde yo sé).
Bother currentUser.secondsLeft y currentUser.lastTimeUpdated son ints. Fundé [[NSDate date] timeIntervalSince1970] como un int porque devuelve un CFTimeInterval que es una typealias de double.
Finalmente, debe asegurarse de guardarlo en una base de datos. Guardo perezosamente estas dos variables en NSUserDefaults cada vez que se actualizan, incluyendo el siguiente código en mi archivo UserData.m:
En UserData.m
-(void)setSecondsLeft:(int)secondsLeft {
_secondsLeft = secondsLeft;
[[NSUserDefaults standardUserDefaults] setObject:@(secondsLeft) forKey:@"secondsLeft"];
}
-(void)setLastTimeUpdated:(int)lastTimeUpdated {
_lastTimeUpdated = lastTimeUpdated;
[[NSUserDefaults standardUserDefaults] setObject:@(lastTimeUpdated) forKey:@"lastTimeUpdated"];
De esta forma, cuando la aplicación se cierra, los datos se guardan para un uso posterior.