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?
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.
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.
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.
Il s'avère que UIViewController
s 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.
myViewController.modalViewController
pointe maintenant vers le modalmyViewController.modalViewController
a commencé, mais myViewController.modalViewController
pointe toujours sur modalmyViewController.modalViewController]
pointe maintenant vers modal deuxmyViewController.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.
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.
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.
[self dismissViewControllerAnimated:YES completion:^{
//Present the new MVC
}];
Remarque: Disponible iOS 5.0 et versions ultérieures.
[self dismissModalViewControllerAnimated:NO];
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
[self presentModalViewController:picker animated:YES];
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.)
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.
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.
En rapide:
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)
}