web-dev-qa-db-fra.com

Présenter un contrôleur de vue modale immédiatement après en avoir renvoyé un autre

Je rejette un contrôleur de vue modale et en présente immédiatement un autre, mais ce dernier ne se produit jamais. Voici le code:

 [auto-licenciementModalViewControllerAnimated: YES]; 

 UIImagePickerController * picker = [[UIImagePickerController alloc] init]; 
 picker.delegate = self; 
 picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; 
 [self presentModalViewController: sélecteur animé: YES]; 

Le premier modal VC glisse vers le bas, mais la nouvelle picker n'apparaît jamais. Une idée de ce qui se passe?

36
James Skidmore

Comme les autres éléments animés, dismissModalViewControllerAnimated ne bloque pas tant que le contrôleur de vue n'a pas disparu. Au lieu de cela, il "déclenche" le renvoi du contrôleur de vue. Vous devrez peut-être utiliser un rappel dans viewDidDisappear du contrôleur modal 1 qui appelle quelque chose comme modalViewControllerDisappeared dans le contrôleur de vue parent. Dans cette méthode, vous présentez le contrôleur modal 2. Sinon, ce que Robot K a dit.

16
Nimrod

Mise à jour d'août 2012:

iOS 5 et versions ultérieures ont introduit des API plus sûres pour faire des choses après que les modaux se soient animés ou non à l'aide de blocs d'achèvement:

[self presentViewController:myModalVC animated:YES completion:^{}];
[self dismissViewControllerAnimated:YES completion:^{}];

Réponse avant août 2012:

J'ai rencontré un problème similaire lors du renvoi du modal puis de la présentation rapide du modal deux. Parfois, les modaux deux apparaissaient après le renvoi du modal et parfois, les modaux deux n'apparaissaient pas du tout, ce qui me rendait très triste.

On dirait une condition de course pour moi ...

Donner un délai de 1 seconde ou plus à l'appelant de la méthode qui présentait le modal deux, showModalTwo, faisait apparaître le modal deux à chaque fois que le modal était rejeté:

- (void)didDismissModalOne {
    [self performSelector:@selector(showModalTwo:) 
               withObject:someNumber 
               afterDelay:1.0f];
}

Cela confirme le soupçon qu'il existe une sorte de condition de concurrence entre le renvoi de modal un et la présentation de modal deux. Mettre un délai sur l'appelant, cependant, est inélégant et ne garantit pas que la condition de concurrence critique ne réapparaîtra pas dans d'autres circonstances.

Le problème

Il s'avère que UIViewControllers a une propriété publique, modalViewController, qui est configurée lorsque presentModalViewController:animated: est appelé et détruite lorsque dismissModalViewControllerAnimated: est appelé. Le problème est qu’elle ne se détruit pas de manière synchrone, il est donc possible de créer une course entre la suppression de l’ancienne valeur de modalViewController et la définition d’une nouvelle valeur de la manière suivante.

  1. Présent modal. myViewController.modalViewController pointe maintenant vers le modal
  2. Rejeter le modal. Le processus en arrière-plan pour démonter myViewController.modalViewController a commencé, mais myViewController.modalViewController pointe toujours sur modal
  3. Présent modal deux, myViewController.modalViewController] pointe maintenant vers modal deux
  4. Le rappel système se déclenche, en définissant myViewController.modalViewController sur nil, cela interrompt le processus d'animation modal deux et le résultat est que l'utilisateur ne le voit jamais.

La course commence à l’étape 2 et se manifeste à l’étape 4.

La solution

Ma solution a été de mettre une condition de garde sur la méthode qui présentait le modal deux pour s'assurer que myViewControoler.modalViewController était nil avant de tenter de présenter le modal deux.

-(void)showModalTwo:(NSNumber *)aParameter {

    if (self.modalViewController) {        
            [self performSelector:@selector(showModalTwo:)
                       withObject:aParameter 
                       afterDelay:0.1f];
            return;
    }
    // You can now present the second modal safely.
}

Travaillé comme un charme. Une solution plus élégante pourrait inclure un délai d'attente.

Post script

Je n’ai vraiment pas aimé l’aspect polling de cette solution. @Nimrod suggère, dans la réponse acceptée à cette question, que vous pouvez lancer en toute sécurité la présentation du modal deux à partir de la méthode viewDidDisappear: de la méthode modale. J'aimais le son de cette approche événementielle, mais après une mise en œuvre complète dans mon cas d'utilisation, j'ai confirmé que la condition de concurrence persistait lors de la présentation de modal deux à l'aide d'un rappel dans viewDidDisappear:. Le seul moyen d'être absolument sûr que le modal deux sera présenté est d'interroger le contrôleur de vue parent jusqu'à ce que vous soyez absolument certain que self.modalViewController est nil. Alors et alors seulement, il est "sûr" de passer au modal deux. 

52
Prairiedogg
[self dismissViewControllerAnimated:YES completion:^{
    //Present the new MVC 

}];

Remarque: Disponible iOS 5.0 et versions ultérieures.

14
Ilker Baltaci
[self dismissModalViewControllerAnimated:NO];

 UIImagePickerController *picker = [[UIImagePickerController alloc] init];
 picker.delegate = self;
 picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
 [self presentModalViewController:picker animated:YES];
5
Sid

Ce qui se passe, c'est que le contrôleur de vue supprime sa référence au contrôleur de vue modal une fois l'animation de renvoi terminée, ce qui se produit après l'appel de ce code. Il ne pense donc pas qu'il doit présenter un nouveau contrôleur de vue de manière modale.

Pour résoudre ce problème, définissez un didDismissModalVC ivar sur YES après avoir appelé dismissModalViewController. Ensuite, dans ma méthode viewDidAppear:, je vérifie la valeur de ivar et présente ensuite le nouveau contrôleur de vue modale. (N'oubliez pas de redéfinir la valeur sur NO afin que je ne reste pas bloqué pour toujours à écarter les contrôleurs de vue modale.)

2
Kris Markel

Dans ce cas, je crée un délégué pour rappeler le contrôleur de vue parent afin d'afficher le deuxième contrôleur de vue modal.

Protocole de définition du contrôleur de vue parent:

@protocol ParentViewControllerDelegate
- (void)showModalTwo;
@end

J'implémente ce protocole dans le contrôleur de vue parent pour afficher le deuxième contrôleur de vue modal et créer la propriété de délégué @property id<ParentViewControllerDelegate> delegate; sur le premier contrôleur de vue modale. 

Affichez le premier contrôleur de vue modal à partir du contrôleur de vue parent:

TheFirstModalViewController *controller = ...
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];
...

Sur la méthode viewDidDisappear: du premier contrôleur de vue modale, appelez simplement delegate.showModalTwo: pour afficher la deuxième vue modale à partir du contrôleur de vue parent.

J'espère que cette aide.

0
ThanhHH

Voici mon approche qui semble bien fonctionner sur iOS 10. Mon cas est légèrement différent, mais devrait fonctionner dans la plupart des situations. Je présente le viewController initial comme un popover qui nécessite qu'un viewController modal soit présenté immédiatement.

Tout d'abord, dans la vue viewDidLoad de viewController initiale, masquez simplement sa vue:

   view.isHidden = true

Ensuite, sur viewWillAppear, présentez la vue modale Contrôleur, à l'unanimité et débloquez la vue à la fin:

   present(yourModalViewController, animated: false) { [unowned self]
       self.view.isHidden = false
    }

Vous voudrez probablement contrôler votre état avec une Bool afin que les appels ultérieurs à viewWillAppear ne présentent plus le modal, mais vous avez compris l'idée.

0
capikaw

En rapide:

  1. UtilisezViewViewController pour fermer la 1ère vue présentée.
  2. Utilisez le bloc completion de licenciementController pour appeler une fonction dans le VC principal. Cette fonction devrait appeler le deuxième VC.

Votre licenciementViewController devrait ressembler à ceci:

var presentingVC_Delegate: mainLists_PopoverDelegation!

@IBAction fund button_Pressed (sender: AnyObject) {
    self.dismissViewControllerAnimated(true, completion: { finished in
        self.presentingVC_Delegate.presentOtherVC()
        print("DismissVC completion block says hello")
    })
}

Où les principales maisons VC présentant l’autre CV:

func presentSettingsVC () {
    self.performSegueWithIdentifier("present_OtherVC", sender: nil)
}
0
Dave G