2009-06-05 7 views
21

J'ai un tabBarController avec deux onglets, dont le premier contient une instance de NavigatorController. Le navigateur navigator est initié avec un viewController personnalisé "peersViewController" qui répertorie tous les homologues réseau sur un tableauView. Lors de la sélection d'un homologue, une instance de "FilesListViewController" (liste des fichiers dans le répertoire c: \) est poussée dans la pile navigationController.UINavigationController popToRootViewController, puis immédiatement pousser une nouvelle vue

Dans ce fichier ListListViewController, j'ai un bouton pour le laisser naviguer vers le répertoire des documents. Pour ce faire, j'avais câblé l'interface pour appeler un gotoDirectory: (NSString *) Méthode de chemin dans le RootViewController:

- (void)gotoDirectory:(NSString*)path { 
    [[self navigationController] popToRootViewControllerAnimated:YES]; 
    NSArray *files = [self getFilesFromPeerAtPath:path]; 
    FilesListViewController *filesVC = [[FilesListViewController alloc] initWithFiles:files]; 
    [[self navigationController] pushViewController:filesVC animated:YES]; 
    [filesVC release]; 
} 

Cependant, lorsque j'appuie sur ce bouton, la NavigationController ne pop mon point de vue sur le contrôleur de vue racine , mais le FilesListViewController que j'ai instancié n'apparaissait pas. À partir du journal, je sais que la méthode personnalisée initWithFiles a bien été appelée et que des éléments réseau sont arrivés pour obtenir les noms de fichiers.

Quelque chose d'autre est vilain à ce sujet. J'ai essayé de cliquer sur le deuxième onglet, puis cliquez sur le premier onglet, et huala! les noms de fichiers dont j'avais besoin sont là. Il semble que les données et le fichier FilesListViewController aient bien été poussés dans la pile navigatorController, mais l'affichage n'a pas été rafraîchi mais bloqué à l'écran de rootViewController (peersViewController).

Est-ce que je fais quelque chose de mal?

--Ben.

- Modifié comme 15 minutes après avoir posté la question. J'avais trouvé une solution de contournement, mais cela me dérange que le pop et le push ne fonctionnent pas.

- (void)gotoDirectory:(NSString*)path { 
    PeersListViewController *rootViewController = (PeersListViewController*)[[[self navigationController] viewControllers] objectAtIndex:0]; 
    [[self navigationController] setViewControllers:[NSArray arrayWithObject:rootViewController]]; 
    FilesListViewController *filesVC = [[FilesListViewController alloc] initWithFiles:files]; 
    [[self navigationController] pushViewController:filesVC animated:YES]; 
    [filesVC release]; 
} 

Il ne de cette façon, et je l'aurais probablement devrait être contournée semble pas comme le NavigationController de libérer tous les viewControllers qui étaient dans la pile d'origine. Cela fonctionne cependant sur le simulateur iphone 3.0.

Si j'utilise ce code cependant, comment la libération de la mémoire devrait-elle être gérée? devrais-je obtenir l'original NSArray de viewcontrollers et libérer tout?

Répondre

10

J'ai eu un problème très similaire (mais sans utiliser d'onglet).

J'ai trois viewController: principal (racine), forme et résultat. lorsque la pile de UINavigationController est

« principale -> résultat »

sur un btnClick Je fais popToRootViewControllerAnimated alors une simple pression sur la formViewCtrl. afin d'avoir

« principale -> forme »

le titre et l'étiquette de barre de navigation bouton retour sont corrects et l'événement du formViewCtrl sont appelés. MAIS, je vois toujours la vue principale.

Voici ma « solution »

Après avoir fait quelques tests, j'ai découvert que sans l'animation pour aller au rootViwCtrl ce beau travail. Donc, je n'utilise que l'animation pour pousser viewCtrl.

iPhone 3.0, problème trouvé sur l'appareil & simulateur.

Si j'ai quelque chose de nouveau, je vais mettre à jour/commenter mon message.

+2

Ouais, vous avez besoin de pop sans animation (de sorte qu'il se passe tout de suite), puis appuyez sur l'animation. Si vous essayez de faire deux changements d'animation ensemble (ce qui prend du temps pour terminer) la vue sera dans un mauvais état. – Jason

+0

Exactement comme décrit, cela ne fonctionne pas pour moi. Je pense que c'est un problème de synchronisation sur les changements de pile (le code d'animation d'Apple est très fragile, il est mal implémenté) - cette technique peut ou ne peut pas fonctionner selon la quantité d'écran restante. – Adam

1

J'ai trouvé une solution de contournement mais je ne peux pas expliquer pourquoi cela fonctionne: 1. D'abord appuyer sur le contrôleur nécessaire. 2. Puis cliquez sur celui que vous voulez.

Ceci est totalement illogique, mais cela fonctionne pour mon cas. Juste pour clarifier les choses, je l'utilise dans le scénario suivant: Premier écran -> Va à l'écran de chargement -> Deuxième écran Lorsque je suis sur le deuxième écran, je ne veux pas avoir l'écran de chargement dans la pile et quand je reviens, je devrais aller au premier écran.

Cordialement, Vesko Kolev

+0

Cela ne devrait vraiment pas fonctionner. Mais ça le fait. Dans 3.0, j'ai trouvé que l'approche classique (manipulant directement le tableau viewControllers) ne fonctionne pas toujours comme prévu, donc j'essaye cette solution de contournement à la place. – Adam

76

Le problème et la solution à ce problème est en fait très simple.

L'appel [self.navigationController popToRootViewControllerAnimated:YES] définit self.navigationController à nil. Lorsque vous appelez par la suite [self.navigationController pushViewController:someOtherViewController], vous envoyez un message au nil, qui ne fait rien.

Pour contourner le problème, il suffit de mettre en place une référence locale à la NavigationController et l'utiliser à la place:

UINavigationController * navigationController = self.navigationController; 
[navigationController popToRootViewControllerAnimated:NO]; 
[navigationController pushViewController:someOtherViewController animated:YES]; 

Comme l'a déclaré Jason, le popToRootViewController doit être effectuée sans l'animation pour que cela fonctionne correctement. Merci d'aller à jpimbert on the Apple forums pour le signaler.

+0

Celui-ci a travaillé pour moi !!! Merci beaucoup ... +1 Pour Nick! – necixy

+1

J'aimerais pouvoir voter pour cela plus d'une fois! Très bonne réponse. – akpb

+0

La bonne solution! –

1

La réponse de Nick Street fonctionne très bien si vous voulez popToRootViewController et ensuite pousser un autre VC.

VC1 -> VC2 -> VC3: cliquez sur le bouton retour de VC3 => VC2, puis VC1, ici OK

Cependant, quand VC1 pousse VC2, qui pousse à son tour VC3, puis retourner à VC1 directement à partir VC3 ne fonctionne pas comme souhaité:

J'ai mis en place dans -(void)viewWillDisappear:(BOOL)animated de VC3:

-(void)viewWillDisappear:(BOOL)animated{ 

    ... 
    [self.navigationController popToRootViewControllerAnimated:YES]; 
} 

J'ai aussi essayé de le mettre en œuvre dans le bouton « retour », même résultat: après avoir heurté le bouton de retour de VC3 pour revenir à VC1: ça casse. Le VC réel est VC1, mais la barre de navigation est toujours VC2. En jouant avec d'autres combinaisons, j'obtiens le NavBar de VC1 sur VC2. Désordre total.

Loda a mentionné quelque chose au sujet de la synchronisation. Je pense que c'est le problème principal ici. J'ai essayé quelques petites choses, alors peut-être que je manque quelque chose ici, mais c'est ce qui a fonctionné pour moi, enfin:

En VC3:

-(void)viewWillDisappear:(BOOL)animated { 

    [super viewWillDisappear:animated]; 
    // notify VC2 
    [[NSNotificationCenter defaultCenter] postNotificationName:backFromV3 object:self]; 
} 

En VC2:

-(void)viewDidLoad { 

    ... 

    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(backFromV3) 
               name:@"BackFromV3" 
               object:nil]; 
} 

-(void)backFromV3{ 
    [NSTimer scheduledTimerWithTimeInterval:0.5 
           target:self 
           selector:@selector(backToRootViewController) 
           userInfo:nil 
           repeats:NO]; 
} 

-(void)backToVC1 { 
    self.navigationItem.rightBarButtonItem = nil; 
    [self.navigationController popToRootViewControllerAnimated:YES]; 
} 

Bien sûr, faites le nettoyage nécessaire.

La minuterie est critique ici. Si 0, il casse. 05 semble bien se passer.

Cela fonctionne parfaitement pour moi. Un peu lourd, mais je n'ai pas réussi à trouver quoi que ce soit qui fasse l'affaire.

1

Vous pouvez conserver l'animation "Retour", suivie de l'animation "Avancer" en retardant fondamentalement l'animation poussée jusqu'à la fin de l'animation pop. Voici un exemple:

(Note: J'ai une variable NSString appelée "transitionTo" dans mon appDelegate initialement définie sur @ "") ... D'abord, définissez cette variable sur NSString que vous pourrez détecter plus tard. Ensuite, pop le contrôleur pour vous donner une transition d'écran agréable retour à la racine:

appDelegate.transitionTo = @"Another"; 
[detailNavigationController popToRootViewControllerAnimated:YES]; 

ensuite dans la classe de l'RootViewController, utilisez la méthode viewDidAppear:

-(void)viewDidAppear:(BOOL)animated 
{ 
    AppDelegate *appDelegate =(AppDelegate*) [UIApplication sharedApplication].delegate; 
    if([appDelegate.transitionTo isEqualToString:@"Another"]) 
    { 
     [self transitionToAnotherView]; 
     appDelegate.transitionTo = @""; 
    } 
} 

-(void)transitionToAnotherView 
{ 
    // Create and push new view controller here 
    AnotherViewController *controller = [[AnotherViewController alloc] init]; 

    UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Home" style:UIBarButtonItemStyleBordered target:nil action:nil]; 
    [self.navigationItem setBackBarButtonItem:backButton]; 

    [[self navigationController] pushViewController:controller animated:YES]; 
} 

Donc, fondamentalement, pop à la racine. ..Quand la transition se termine à "viewDidAppear" ... puis appuyez sur la vue suivante. Il m'est arrivé de garder une variable pour vous dire quelle vue vous souhaitez faire passer (avec @ "" signifiant ne pas faire de transition dans le cas où je veux rester sur cet écran).

3

Je vois que cette question à propos de popping à la racine, puis en poussant un nouveau ViewController est très répandue, et ce poste est vu beaucoup, donc je voulais ajouter mon bit pour aider d'autres nouveaux gars, en particulier ceux qui utilisent Xcode 4 et un storyboard.

Dans Xcode 4, vous avez un storyboard. Disons que vous avez ces contrôleurs de vue: HomeViewController, FirstPageViewController, SecondPageViewController. Assurez-vous de cliquer sur chacun d'eux et nommez leurs identifiants en allant dans l'onglet Utilitaires -> Attributs. Nous dirons qu'ils sont nommés Accueil, Premier et Deuxième.

Vous êtes à la maison, alors vous allez en premier, alors vous voulez être en mesure d'aller à la deuxième et être en mesure d'appuyer sur le bouton retour pour retourner à la maison. Pour ce faire, vous voulez changer votre code dans FirstPageViewController.

Pour développer l'exemple, créez un bouton dans FirstPageViewController dans le storyboard. Ctrl-faites glisser ce bouton dans FirstPageViewController.m. Là, le code suivant atteindre le résultat souhaité:

// Remember to add #import "SecondPageViewController.h" at the top 
    SecondPageViewController *secondView = [self.storyboard instantiateViewContorllerWithIdentifier:@"Second"]; 
    UINavigationController *navigationController = self.navigationController; 
    NSArray *array = [navigationController viewControllers]; 
    // [array objectAtIndex:0] is the root view controller 
    NSArray *viewControllersStack = [NSArray arrayWithObjects:[array objectAtIndex:0], secondView, nil]; 
    [navigationController setViewControllers:viewControllersStack animated:YES]; 

Fondamentalement, vous saisissant les contrôleurs de vue, les disposer dans une pile dans l'ordre que vous voulez, puis avoir l'utilisation du contrôleur de navigation pile pour la navigation. C'est une alternative à pousser et à éclater.

Questions connexes