ios - iOS 中的倒数计时器在后台不工作

标签 ios objective-c background nstimer

我已经使用 UIDatePickerNSTimeInterval 实现了一个简单的倒计时并且它工作正常,但是我有以下问题:当我在 Xcode 模拟器中运行应用程序时,如果我按下 Ctrl + shift + h,倒计时在后台运行,但是当我在我的 iPhone 6 上运行该应用程序并按下主页按钮时,倒计时停止并且它在后台不起作用.

我已经实现了以下通知 (AppDelegate.m):

- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.

    [[NSNotificationCenter defaultCenter] postNotificationName:@"didEnterBackground" object:nil];

}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    [[NSNotificationCenter defaultCenter] postNotificationName:@"didEnterForeground" object:nil];

}

ViewController.m中的代码是:

- (void)viewDidLoad {

    self.mensajeCuenta.hidden = YES;
    self.botonDetenerCuenta.hidden = YES;

    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enteredBackground:) name:@"didEnterBackground" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enteredForeground:) name:@"didEnterForeground" object:nil];
    estaActivo = false;
    cuentaAtras = 0.0;

}

- (void)viewDidUnload
{
    [super viewDidUnload];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    // Release any retained subviews of the main view.
    if ( [tiempo isValid] ) {
        [tiempo invalidate];
    }
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    } else {
        return YES;
    }
}

- (IBAction)botonIniciarCuenta:(id)sender {



    cuentaAtras = (NSTimeInterval)_vistaContador.countDownDuration;
    remainder = cuentaAtras;
    //NSLog(@"Total de segundos: %i", remainder);
    afterRemainder = cuentaAtras - remainder%60;
    //NSLog(@"Total segundos despues restantes: %i", afterRemainder);


    tiempo = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(actualizarCuentaAtras) userInfo:nil repeats:YES];

}

- (IBAction)botonDetenerCuenta:(id)sender {


    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"La cuenta atrás se ha parado" message:@"Pulse Iniciar para una nueva cuenta" preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"Aceptar" style:UIAlertActionStyleDefault
                                                          handler:^(UIAlertAction * action) {}];

    [alert addAction:defaultAction];
    [self presentViewController:alert animated:YES completion:nil];

    [self visibilidadBotones];
    [tiempo invalidate];
    tiempo = nil;


}

- (void)actualizarCuentaAtras {

    self.botonIniciarCuenta.hidden = YES;
    self.mensajeCuenta.hidden = NO;
    self.tiempoRestante.hidden = NO;
    self.botonDetenerCuenta.hidden = NO;

    dispatch_async(dispatch_get_main_queue(), ^{

    if (afterRemainder >= 0) {
        afterRemainder--;


        //NSLog(@"Valor restante disminuido: %i", afterRemainder);

        int horas = (int)(afterRemainder/(60*60));
        int minutos = (int)(((int)afterRemainder/60)- (horas * 60));
        int segundos = (int)(((int)afterRemainder - (60 * minutos) - (60 * horas * 60)));

        NSString *cadenaTiempo = [[NSString alloc]initWithFormat:@"%02u : %02u : %02u", horas, minutos, segundos];

        self.tiempoRestante.text = cadenaTiempo;

        if (afterRemainder == 0) {

            [tiempo invalidate];
            tiempo = nil;
            [self visibilidadBotones];
            [self enviarAlerta];
        }
    }

    });



}

- (void)enteredBackground:(NSNotification *)notification
{
    if (estaActivo) {
        [tiempo invalidate];
        date = [NSDate dateWithTimeIntervalSinceNow:cuentaAtras];
        //NSLog([date description]);
        self.notification = [[UILocalNotification alloc] init];
        self.notification.fireDate = date;
        self.notification.timeZone = [NSTimeZone defaultTimeZone];
        self.notification.alertAction = @"timer fired";
        self.notification.alertBody = @"timer fired!";
        self.notification.soundName = UILocalNotificationDefaultSoundName;
        [[UIApplication sharedApplication] scheduleLocalNotification:self.notification];
    }
}

- (void)enteredForeground:(NSNotification *)notification
{
    if (estaActivo) {
        NSTimeInterval newDuration = [self.notification.fireDate timeIntervalSinceNow];
        if (newDuration > 0.0) {
            cuentaAtras = newDuration;
            tiempo = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(actualizarCuentaAtras) userInfo:nil repeats:YES];
        } else {
            cuentaAtras = 0.0;
            //[self.countDownPicker setHidden:NO];
            //[self.countDownLabel setHidden:YES];
            estaActivo = !estaActivo;
        }
        [self actualizarCuentaAtras];
        [[UIApplication sharedApplication] cancelLocalNotification:self.notification];
    }
}

- (void)enviarAlerta {


    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Notificación enviada" message:@"Puede reiniciar la cuenta atrás" preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"Aceptar" style:UIAlertActionStyleDefault
                                                          handler:^(UIAlertAction * action) {}];

    [alert addAction:defaultAction];
    [self presentViewController:alert animated:YES completion:nil];
}

-(void)visibilidadBotones {

    self.botonIniciarCuenta.hidden = NO;
    self.botonDetenerCuenta.hidden = YES;
    self.mensajeCuenta.hidden = YES;
    self.tiempoRestante.hidden = YES;

}
@end

ViewController.h中的代码是:

@interface ViewController : UIViewController {

    int afterRemainder;
    int remainder;
    NSTimeInterval cuentaAtras;
    NSTimer *tiempo;
    BOOL estaActivo;
    NSDate *date;
}


@property (strong, nonatomic) IBOutlet UIDatePicker *vistaContador;

@property (strong, nonatomic) IBOutlet UILabel *mensajeCuenta;

- (IBAction)botonIniciarCuenta:(id)sender;
@property (strong, nonatomic) IBOutlet UIButton *botonIniciarCuenta;


- (IBAction)botonDetenerCuenta:(id)sender;
@property (strong, nonatomic) IBOutlet UIButton *botonDetenerCuenta;

@property (strong, nonatomic) IBOutlet UILabel *tiempoRestante;

@property (strong, nonatomic) UILocalNotification *notification;

我开始在 iOS 中编程,后台控制过程对我来说非常复杂,但我不明白为什么倒计时在 Xcode 模拟器中有效,但在我的 iPhone 上却无效?

怎么了?

最佳答案

除非您利用其他一些后台模式,否则计时器不会在后台运行。

处理此问题的最佳方法是在 applicationWillResignActive 中暂停它并记下当前时间,然后在 applicationDidBecomeActive 中重新启动它,减去自已暂停。

关于ios - iOS 中的倒数计时器在后台不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34633471/

相关文章:

ios - 用于激活应用程序的 Apple IOS 电源按钮联动

objective-c - 如何在 OSX 上基于文档的应用程序中导出包文件?

ios - 在 iOS 8 中,如何在 la Mail.app 中实现长扫删除手势

java - 更改 JButton 的背景颜色始终显示为灰色

powershell - Powershell 中的后台作业

ios - Swift 4-应用程序在按下启动 segue 的按钮时崩溃

ios - 如何从其 App Extension 启动父 iOS App

ios - 'type' 不是 'T' 的成员类型

ios - 如何使用 Restkit 0.20.0 创建/发布新的托管对象到服务器?

html - 如何为 anchor 标签添加背景?