6

estoy totalmente perplejo, aquí está la situación:¿Por qué popViewController sólo funcionan todas las otras veces

Mi aplicación utiliza el marco Ubicación Core para obtener la ubicación actual del usuario y luego se hace un ping a mi servidor en TrailBehind para los lugares de interés cercanos y los muestra como una lista. No hay problemas.

Para conservar las baterías, apago el servicio de GPS después de obtener mis datos del servidor. Si el usuario se mueve mientras usa la aplicación y quiere una nueva lista, hace clic en "Actualizar" en el controlador de navegación y el servicio CLLocation se activa de nuevo, se recupera un nuevo lote de datos del servidor y se vuelve a dibujar la tabla.

Mientras la aplicación está captando datos de mi servidor, cargo una pantalla de carga con un globo giratorio que dice "Cargando, espere" y oculto la barra de navegación para que no den "atrás".

Por lo tanto, la captura de datos inicial del servidor se realiza sin problemas.

La PRIMERA vez que pulso actualizar todo el código se ejecuta para obtener una nueva ubicación, hacer un nuevo ping al servidor para obtener una nueva lista de datos y actualizar las celdas. Sin embargo, en lugar de cargar la vista de tabla como debería, restaura la barra del controlador de navegación para la vista de tabla pero aún muestra mi vista de carga en la ventana principal. Esto solo es cierto en el dispositivo, todo funciona perfectamente en el simulador.

La SEGUNDA vez que pulso actualizar la función funciona normalmente.

La TERCERA vez que pulso actualizar falla como se indica anteriormente.

La CUARTA vez que pulso actualizar funciona normalmente.

La QUINTA vez que pulso actualizar falla como se indica arriba.

etc etc., incluso las actualizaciones tienen éxito y las actualizaciones impares fallan. Pasé por encima de todo mi código línea por línea y todo parece ejecutarse normalmente. De hecho, continué pisando las instrucciones básicas y después de una gran cantidad de clics "paso adelante" encontré que la vista de tabla realmente aparece en la pantalla en algún momento en CFRunLoopRunSpecific, pero luego hice clic en "continuar" y mi vista de carga se hizo cargo la pantalla.

Estoy absolutamente desconcertado. ¡¡Por favor ayuda!! Muchas gracias de antemano por su visión.

Video of the strange behavior:

código relevante:

RootViewControllerMethods (Esta es la vista base para este proyecto TableView)

- (void)viewDidLoad { 
    //Start the Current Location controller as soon as the program starts. The Controller calls delegate methods 
    //that will update the list and refresh 
    [MyCLController sharedInstance].delegate = self; 
    [[MyCLController sharedInstance].locationManager startUpdatingLocation]; 
    lv = [[LoadingViewController alloc] initWithNibName:@"Loading" bundle:nil]; 
    [self.navigationController pushViewController:lv animated:YES]; 
    [super viewDidLoad]; 
} 



- (void)updateClicked { 
    //When the location is successfully updated the UpdateCells method will stop the CL manager from updating, so when we want to update the location 
    //all we have to do is start it up again. I hope. 
    [[MyCLController sharedInstance].locationManager startUpdatingLocation]; 
    [self.navigationController pushViewController:lv animated:YES]; 
    //LV is a class object which is of type UIViewController and contains my spinning globe/loading view. 
} 



-(void)updateCells { 
    //When the Core Location controller has updated its location it calls this metod. The method sends a request for a JSON dictionary 
    //to trailbehind and stores the response in the class variable jsonArray. reloadData is then called which causes the table to 
    //re-initialize the table with the new data in jsonArray and display it on the screen. 

    [[MyCLController sharedInstance].locationManager stopUpdatingLocation]; 

    if(self.navigationController.visibleViewController != self) { 
     self.urlString = [NSString stringWithFormat:@"http://www.trailbehind.com/iphone/nodes/%@/%@/2/10",self.lat,self.lon]; 
     NSURL *jsonURL = [NSURL URLWithString:self.urlString]; 
     NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL]; 
     NSLog(@"JsonData = %@ \n", jsonURL); 
     self.jsonArray = [jsonData JSONValue]; 
     [self.tableView reloadData]; 
     [self.navigationController popToRootViewControllerAnimated:YES]; 
     [jsonData release]; 
    } 
} 

Métodos CLController: Básicamente, sólo envía todos los datos directamente de vuelta a la RootViewController

// Called when the location is updated 
- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
      fromLocation:(CLLocation *)oldLocation 
{ 
    NSLog(@"New Location: %@ \n", newLocation); 
    NSLog(@"Old Location: %@ \n", oldLocation); 
    @synchronized(self) { 
     NSNumber *lat = [[[NSNumber alloc] init] autorelease]; 
     NSNumber *lon = [[[NSNumber alloc] init] autorelease]; 
     lat = [NSNumber numberWithFloat:newLocation.coordinate.latitude]; 
     lon = [NSNumber numberWithFloat:newLocation.coordinate.longitude]; 
     [self.delegate noteLat:lat]; 
     [self.delegate noteLon:lon]; 
     [self.delegate noteNewLocation:newLocation]; 
     [self.delegate updateCells]; 
    } 
} 
+0

¿Has pasado por estos métodos en el depurador? Ejecutar instrumentos para ver si la creación/eliminación de objetos es lo que esperas? Disculpa si insulta tu inteligencia, pero como no veo el problema de un vistazo rápido, lo siguiente que hago es comenzar a ... – mikeh

+0

¿Ocurre algo así si sales de la aplicación? Parece que no vas a dejar la aplicación el tiempo suficiente para obtener una corrección de GPS en el video. Intente establecer puntos de interrupción en los métodos de locationManager. –

Respuesta

0

¿Puedes probar y depurar tu aplicación para ver a dónde va el control cuando llamas a updateCells? No parece ser nada aparentemente incorrecto con la aplicación.

Asegúrese de que no haya advertencias de memoria mientras se encuentre en la clase LoadingViewController. Si hay una advertencia de memoria y se está liberando la vista de RootViewController, se llamará de nuevo a viewDidLoad cuando haga un pop a RootViewController.

Mantenga puntos de interrupción en viewDidLoad y updateCells. ¿Seguro que no estás llamando a LoadingViewController a otro lado?

+0

He puesto puntos de interrupción en ambas funciones, y viewDidLoad no se llama cuando se abre la pantalla de carga. Tampoco se está llamando a ninguna de mis funciones didRecieveMemoryError. Después de llamar a updateCells, el control vuelve a la pila del núcleo, y creo que algo allí puede estar jugando conmigo. –

1

Lo primero que se piensa es que puede que no desee enviar startUpdatingLocation al CLLocationManager hasta después de que haya empujado su vista de carga. A menudo, el primer mensaje de ubicación de ubicación: didUpdateToLocation: fromLocation: aparecerá de manera instantánea con datos de GPS en caché. Esto solo importa si está actuando en cada mensaje y no filtra los datos de GPS como se muestra en el código de muestra aquí. Sin embargo, esto no provocaría la situación que ha descrito: podría hacer que la pantalla de carga se atasque.

He experimentado un comportamiento similarmente extraño como este en una situación diferente en la que estaba tratando de pasar al controlador de vista raíz al cambiar a una pestaña diferente y la llamada no se realizó en el lugar correcto. Creo que el popToRootViewController fue llamado dos veces para mí. Mi sospecha es que su vista de carga es presionada dos veces o reventada dos veces.

Recomiendo implementar -viewWillAppear :, -viewDidAppear :, -viewWillDisappear: y -viewDidDisappear: con un inicio de sesión mínimo en su LoadingViewController.

- (void)viewWillAppear:(BOOL)animated { 
    NSLog(@"[%@ viewWillAppear:%d]", [self class], animated); 
    [super viewWillAppear:animated]; 
} 

- (void)viewDidAppear:(BOOL)animated { 
    NSLog(@"[%@ viewDidAppear:%d]", [self class], animated); 
    [super viewDidAppear:animated]; 
} 

- (void)viewWillDisappear:(BOOL)animated { 
    NSLog(@"[%@ viewWillDisappear:%d]", [self class], animated); 
    [super viewWillDisappear:animated]; 
} 

- (void)viewDidDisappear:(BOOL)animated { 
    NSLog(@"[%@ viewDidDisappear:%d]", [self class], animated); 
    [super viewDidDisappear:animated]; 
} 

A continuación, ejecute una prueba en el dispositivo para ver si ellos siempre están siendo enviados a su controlador de vista y con qué frecuencia. Puede agregar un poco de registro a -updateClicked para revelar dos toques.

Otra idea, aunque su bloque @synchronized es una buena idea, solo evitará que otros subprocesos ejecuten esas declaraciones hasta que el primer hilo salga del bloque. Sugiero mover el mensaje -stopUpdatingLocation para que sea la primera declaración dentro de ese bloque @synchronized. De esta forma, una vez que decide actuar sobre algunos datos nuevos de GPS, inmediatamente le dice a CLLocationManager que deje de enviar datos nuevos.

0

Por lo tanto, nunca hice que esto funcione. Observo este comportamiento en el dispositivo cada vez que llamo a popViewController programáticamente en lugar de permitir que el botón de retroceso predeterminado en el controlador de navegación haga el popping.

Mi solución consistía en crear una vista de carga personalizada y voltear la pantalla a esa vista cada vez que hubiera un retraso debido al acceso a Internet. Mi método toma una variable booleana de sí o no - sí cambia a la pantalla de carga y no cambia a la vista normal. Aquí está el código:

- (void)switchViewsToLoading:(BOOL)loading { 
// Start the Animation Block 
CGContextRef context = UIGraphicsGetCurrentContext(); 
[UIView beginAnimations:nil context:context]; 
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:self.tableView cache:YES]; 
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 
[UIView setAnimationDuration:.75]; 

// Animations 
if(loading) { 
    if (lv == nil) { lv = [[LoadingViewController alloc] initWithNibName:@"Loading" bundle:nil]; } 
    [self.view addSubview:lv.view]; 
    [self.view sendSubviewToBack:self.tableView]; 
    self.title = @"TrailBehind"; 
} 
else { 
    [lv.view removeFromSuperview]; 

} 
// Commit Animation Block 
[UIView commitAnimations]; 
//It looks kind of dumb to animate the nav bar buttons, so set those here 
if(loading) { 
    self.navigationItem.rightBarButtonItem = nil; 
    self.navigationItem.leftBarButtonItem = nil; 
    self.title = @"TrailBehind"; 
} 
else { 
    UIBarButtonItem *feedback = [[UIBarButtonItem alloc] initWithTitle:@"Feedback" style:UIBarButtonItemStylePlain target:self action:@selector(feedbackClicked)]; 
    self.navigationItem.rightBarButtonItem = feedback; 
    UIBarButtonItem *update = [[UIBarButtonItem alloc] initWithTitle:@"Move Me" style:UIBarButtonItemStylePlain target:self action:@selector(updateClicked)]; 
    self.navigationItem.leftBarButtonItem = update; 
    [feedback release]; 
    [update release]; 
} 

}

0

En cuanto a su código original, sospecho que este bloque mucho:

- (void)viewDidLoad { 
    ... 
    lv = [[LoadingViewController alloc] initWithNibName:@"Loading" bundle:nil]; 
    [self.navigationController pushViewController:lv animated:YES]; 
    [super viewDidLoad]; 
} 

viewDidLoad se llama cada vez que se carga la SEMILLA, lo cual puede suceder múltiples veces, especialmente si se queda sin memoria (algo que parece probable dado su comentario de que solo ocurre en el dispositivo). Recomiendo que implemente -didReciveMemoryWarning, y después de llamar a super, al menos imprima un registro para que pueda ver si le sucede a usted.

Lo que me molesta del código anterior es que casi con total seguridad está filtrando lv, lo que significa que puede haber un número creciente de LoadingViewControllers corriendo. Usted dice que es una variable de clase. ¿Realmente quieres decir que es una variable de instancia? ivars siempre debe usar accessors (self.lv o [self lv] en lugar de lv). No les asignes directamente; casi siempre lo harás mal (ya que es probable que estés aquí).

0

Me encontré con esto mientras buscaba exactamente el mismo problema, por lo que aunque estoy seguro de que ya ha resuelto su problema, pensé en publicar mi solución en caso de que alguien más la encuentre ...

Este error parece deberse a que asigna dos IBActions al mismo UIButton en el constructor de interfaz. Resultó que el botón que utilicé para presionar el controlador de vista en la pila fue asignado a dos IBActions, y cada uno empujaba un controlador diferente a la pila de navigationController (aunque solo terminarás viendo uno de ellos, quizás el último uno para ser llamado). De todos modos, presionar el botón Atrás en la vista superior en realidad no lo descarta (o tal vez está descartando el segundo controlador invisible), y tienes que presionar dos veces para volver.

De todos modos, revise sus botones y asegúrese de que solo estén asignados a una única IBAction. Eso lo solucionó para mí.

Cuestiones relacionadas