J'ai un problème et je l'ai décrit ci-dessous.
J'utilise UIViewControllerContextTransitioning pour des transitions personnalisées.
J'ai 2 contrôleurs de vue, premier contrôleur de vue et deuxième contrôleur de vue.
Maintenant, je veux ajouter un contrôleur secondview sur le premier contrôleur de vue avec animation. Je l'ai atteint, j'ai maintenant le contrôleur secondview transparent, afin que nous puissions voir le premier contrôleur de vue sous le contrôleur de secondview.
Mais je ne suis pas en mesure de voir le premier contrôleur de vue, et je ne peux voir qu'un écran noir en dessous du contrôleur de secondview.
Voici le code.
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
self.transitionContext = transitionContext;
if(self.isPresenting){
[self executePresentationAnimation:transitionContext];
}
else{
[self executeDismissalAnimation:transitionContext];
}
}
-(void)executePresentationAnimation:(id<UIViewControllerContextTransitioning>)transitionContext{
UIView* inView = [transitionContext containerView];
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
CGRect offScreenFrame = inView.frame;
offScreenFrame.Origin.y = inView.frame.size.height;
toViewController.view.frame = offScreenFrame;
toViewController.view.backgroundColor = [UIColor clearColor];
fromViewController.view.backgroundColor = [UIColor clearColor];
inView.backgroundColor = [UIColor clearColor];
[inView insertSubview:toViewController.view aboveSubview:fromViewController.view];
// [inView addSubview:toViewController.view];
CFTimeInterval duration = self.presentationDuration;
CFTimeInterval halfDuration = duration/2;
CATransform3D t1 = [self firstTransform];
CATransform3D t2 = [self secondTransformWithView:fromViewController.view];
[UIView animateKeyframesWithDuration:halfDuration delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.5f animations:^{
fromViewController.view.layer.transform = t1;
}];
[UIView addKeyframeWithRelativeStartTime:0.5f relativeDuration:0.5f animations:^{
fromViewController.view.layer.transform = t2;
}];
} completion:^(BOOL finished) {
}];
[UIView animateWithDuration:duration delay:(halfDuration - (0.3*halfDuration)) usingSpringWithDamping:0.7f initialSpringVelocity:6.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
toViewController.view.frame = inView.frame;
} completion:^(BOOL finished) {
[self.transitionContext completeTransition:YES];
}];
}
Quand [self.transitionContext completeTransition:YES];
appelé, le premier contrôleur de vue disparaît soudainement et un écran noir s'affiche sous le second contrôleur de vue.
Quelqu'un a-t-il une idée? Merci.
J'avais le même problème ici - cela ressemble à un bogue dans iOS 8. J'ai déposé un radar .
J'ai utilisé Reveal pour inspecter la hiérarchie des vues une fois que l'écran est noir. La clé UIWindow
est complètement vide - aucune hiérarchie de vue!
J'ai joué un peu et il semble y avoir une solution de contournement facile, pour des cas simples. Vous pouvez simplement rajouter la vue toViewController
en tant que sous-vue de la fenêtre de clé:
transitionContext.completeTransition(true)
UIApplication.sharedApplication().keyWindow!.addSubview(toViewController.view)
J'ai vérifié et la fenêtre de clé rootViewController
est toujours correctement définie, donc c'est bien. Je ne sais pas ce qui se passerait si vous présentiez votre contrôleur à partir d'un contrôleur modal déjà présenté. Par conséquent, pour les cas plus complexes, vous devrez faire des essais.
Je pense que le raisonnement derrière cela devrait être mieux expliqué.
La vue disparaît car vous enlevez la vue du contrôleur de présentation de son emplacement d'origine (hiérarchie de vues), vous la placez dans la vue conteneur que votre animateur fournit, mais ne la restituez jamais après la fin de l'animation. Ainsi, la vue de ce contrôleur de vue est entièrement supprimée de la fenêtre avec son aperçu (containerView).
Dans iOS 7, le système renvoyait toujours les vues des contrôleurs de vue impliqués dans la présentation (présentation et présentation) à leurs emplacements d'origine après l'animation automatique de la transition. Cela ne se produit plus pour certains styles de présentation dans iOS 8.
La règle est très simple: l'animateur ne doit manipuler la vue du contrôleur de présentation que si celle-ci est masquée (supprimée de la hiérarchie de la vue) complètement par fin de transition. En d'autres termes, cela signifie qu'après la fin de l'animation de présentation initiale, seule la vue du contrôleur de vue présentée sera visible et non celle du contrôleur de présentation. Par exemple, si vous définissez l'opacité de la vue du contrôleur de la vue présentée sur 50% et utilisez UIModalPresentationFullScreen, vous ne pourrez pas voir la vue du contrôleur de la présentation sous celle présentée, mais si vous utilisez la méthode UIModalPresentationOverFullscreen, vous (la méthode shouldRemovePresentersView
de UIPresentationController est responsable) en précisant que).
Pourquoi ne pas permettre à l'animateur de manipuler à tout moment la vue du contrôleur de présentation? Tout d'abord, si la vue du contrôleur de la vue présentation doit rester visible après la fin de l'animation pendant tout le cycle de vie de la présentation, il n'est pas nécessaire de l'animer du tout, elle reste simplement là où elle se trouve. Deuxièmement, si la propriété de ce contrôleur de vue est transférée vers le contrôleur de présentation, le contrôleur de présentation ne saura probablement pas comment mettre en page la vue de ce contrôleur de vue si nécessaire, par exemple lorsque l'orientation change, mais le propriétaire d'origine du contrôleur de présentation .
Dans iOS 8 viewForKey:
_ Une méthode a été introduite pour obtenir les vues manipulées par l'animateur. Premièrement, il est utile de suivre la règle décrite ci-dessus en retournant nil chaque fois que l'animateur ne doit pas toucher la vue. Deuxièmement, il peut renvoyer une vue différente à l'animateur. Imaginez que vous implémentez une présentation similaire à une feuille de formulaire. Dans ce cas, vous voudriez ajouter une ombre ou une décoration autour de la vue du contrôleur de vue présenté. L'animateur animera cette décoration à la place et la vue du contrôleur de vue présenté sera un enfant de la décoration.
viewControllerForKey:
ne disparaît pas, il peut toujours être utilisé si un accès direct aux contrôleurs de vue est nécessaire, mais l’animateur ne doit pas présumer des vues qu’il doit animer.
Il y a plusieurs choses que vous pouvez faire pour correctement corrigez un problème lié à la disparition de la vue du contrôleur de la vue de présentation lorsque vous le placez explicitement dans la vue du conteneur de l'animateur:
Si vous n'avez pas besoin d'animer la vue du contrôleur de présentation, utilisez viewForKey:
pour que les vues s'animent au lieu de rechercher directement les vues du contrôleur. viewForKey:
peut renvoyer des vues nulles, voire complètement différentes.
Si vous souhaitez animer la vue des contrôleurs de présentation, vous devez utiliser le style UIModalPresentationFullScreen
ou continuer à utiliser UIModalPresentationCustom
et implémentez votre propre sous-classe de UIPresentationController avec shouldRemovePresentersView
renvoyant YES
. En fait, la mise en œuvre de cette méthode constitue la principale différence entre les contrôleurs de présentation internes définis par les styles UIModalPresentationFullScreen
et UIModalPresentationCustom
, mis à part le fait que ce dernier vous permet d'utiliser des contrôleurs de présentation personnalisés.
Dans tous les autres cas rares, vous devrez ramener la vue du contrôleur de la vue de présentation à son emplacement d'origine, comme suggéré par d'autres réponses.
Dans iOS 8, vous devez manipuler les vues renvoyées par viewForKey:
à la place du .view
propriété des contrôleurs de vue renvoyée par viewControllerForKey:
. Cela n’est pas particulièrement clair dans la documentation de la version bêta, mais si vous examinez le code source de UIViewControllerTransitioning.h, vous verrez ce commentaire ci-dessus viewControllerForKey:
:
// Currently only two keys are defined by the
// system - UITransitionContextToViewControllerKey, and
// UITransitionContextFromViewControllerKey.
// Animators should not directly manipulate a view controller's views and should
// use viewForKey: to get views instead.
- (UIViewController *)viewControllerForKey:(NSString *)key;
Donc, au lieu de régler les images, etc. de toViewController.view
, utilisez la valeur de retour de [transitionContext viewForKey:UITransitionContextToViewKey]
.
Si votre application doit prendre en charge iOS7 et/ou Xcode 5, vous pouvez utiliser une méthode de catégorie simple sur UIViewController, comme suit:
- (UIView *)viewForTransitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
NSString *key = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey] == self ? UITransitionContextFromViewKey : UITransitionContextToViewKey;
return [transitionContext viewForKey:key];
} else {
return self.view;
}
#else
return self.view;
#endif
}
Ensuite, récupérez votre toViewController
et fromViewController
comme d'habitude, mais récupérez les vues à l'aide de [toViewController viewForTransitionContext:transitionContext]
.
Edit: Il semble y avoir un bogue dans lequel la vue du contrôleur de la vue présentation est nulle lorsqu’elle est renvoyée de viewForKey
, ce qui vous empêche de créer des transitions modales qui animent la vue (telles que le glissement ou le retournement). horizontal). J'ai déposé un bogue pour iOS8 à l'adresse rdar: // 17961976 ( http://openradar.appspot.com/radar?id=5210815787433984 ). Voir également l'exemple de projet à l'adresse http://github.com/bcherry/TransitionBug
Edit 2: Merci à graveley pour cette suggestion, l'utilisation de UIModalPresentationFullScreen corrige le problème. Ce n'est peut-être pas un bug. Apple peut vouloir que UIModalPresentationCustom ne modifie que la vue du modal entrant. Si vous souhaitez modifier la vue sortante, vous devez garantir la présentation de la nouvelle vue en plein écran. Dans tous les cas, vous devez utilisez viewForKey
et UIModalPresentationFullScreen.
Ne pas définir modalPresentationStyle
sur UIModalPresentationCustom a corrigé le problème pour moi.
En d'autres termes, laisser à la valeur par défaut de UIModalPresentationFullScreen au lieu de spécifier UIModalPresentationCustom a résolu le problème de la vue qui disparaissait. Notez que le protocole UIViewControllerTransitioningDelegate semble toujours être suivi même si vous le laissez par défaut. Si je me souviens bien, il était une fois UIModalPresentationCustom était une exigence.
Cela fonctionne jusqu’à présent, je n’ai essayé cela que pour des animations non interactives.
J'ai trouvé cette réponse extrêmement utile dans un fil de discussion de Lefteris: https://stackoverflow.com/a/27165723/370917
Résumer:
+1 dans votre transition personnalisée, n'ajoutez pas toView lorsque l'animation de renvoi est en cours.
Démontré ici:
https://www.dropbox.com/s/7rpkyamv9k9j18v/CustomModalTransition.zip?dl= sans aucun piratage! c'est comme par magie! :)
Dans iOS 8, vous devez créer un UIPresentationController et implémenter la méthode ci-dessous, dans UIViewControllerTransitioningDelegate.
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source;
Demande à votre délégué d'utiliser le contrôleur de présentation personnalisé pour gérer la hiérarchie de vues lors de la présentation d'un contrôleur de vue.
Valeur de retour:
Le contrôleur de présentation personnalisé permettant de gérer la présentation modale.
Discussion:
Lorsque vous présentez un contrôleur de vue à l'aide du style de présentation UIModalPresentationCustom, le système appelle cette méthode et demande le contrôleur de présentation qui gère votre style personnalisé. Si vous implémentez cette méthode, utilisez-la pour créer et renvoyer l'objet de contrôleur de présentation personnalisé que vous souhaitez utiliser pour gérer le processus de présentation.
Si vous n'implémentez pas cette méthode ou si l'implémentation de cette méthode renvoie nil, le système utilise un objet contrôleur de présentation par défaut. Le contrôleur de présentation par défaut n'ajoute aucune vue ni contenu à la hiérarchie des vues.
Disponibilité Disponible dans iOS 8.0 et versions ultérieures.
Pour plus d'informations, visionnez la vidéo WWDC 2014:
https://developer.Apple.com/videos/wwdc/2014/?include=228
Il existe également un exemple de code de la WWDC intitulé "LookInside: adaptabilité des contrôleurs de présentation et objets Custom Animator", que vous pouvez télécharger à partir de la page de code d'exemple WWDC 2014.
Vous devrez peut-être modifier un peu l'exemple de code. La méthode init UIPresentationController a été modifiée en:
initWithPresentedViewController:presented presentingViewController:presenting
Avant il a été présenté et ensuite présenté. Il suffit de les échanger et cela devrait fonctionner.
Voici une version Objective C du correctif de Ash.
// my attempt at obj-c version of Ash's fix
UIView *theToView = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view;
[[[UIApplication sharedApplication] keyWindow] addSubview:theToView];
[transitionContext completeTransition:YES]
Je devais échanger la commande et appeler la méthode [transitionContext completeTransition:] après l'ajout de la vue afin de présenter un nouveau contrôleur de vue à partir du bloc d'achèvement de la suppression d'un autre contrôleur de vue afin de fonctionner correctement.
Je ne sais pas si cela résoudra le problème pour tout le monde, mais cela fonctionne dans mon application. À votre santé!
au lieu de [inView insertSubview: toViewController.view aboveSubview: fromViewController.view]; ajoutez simplement: [inView addSubview: toViewController.view];
if (self.presenting) {
[transitionContext.containerView addSubview:toViewController.view];
// your code
} else {
// your code
}
Vous pouvez voir un exemple ici: lien et cela fonctionne sur iOS 7 et iOS 8
Je l'ai trouvé viewForKey:UITransitionContextToViewKey
renvoie zéro sur ios8. Donc, si c'est nul, je saisis la vue depuis le contrôleur de vue.
Cependant, il semble que la vue "to" ne soit pas déplacée du conteneur vers la fenêtre lorsque completeTransition:YES
est appelé. Donc si viewForKey:UITransitionContextToViewKey
renvoie zéro, je tombe à toVC.view
, et garder trace du fait qu'il est retourné à zéro, et après l'achèvement, je le déplace vers la vue d'ensemble initiale du conteneur (qui se trouve être la fenêtre).
Donc, ce code fonctionne aussi bien sur iOS7 que sur iOS8, et devrait fonctionne aussi sur iOS9 même s’ils le corrigent ou non.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
// Get the 'from' and 'to' views/controllers.
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
BOOL hasViewForKey = [transitionContext respondsToSelector:@selector(viewForKey:)]; // viewForKey is iOS8+.
UIView *fromView = hasViewForKey ?
[transitionContext viewForKey:UITransitionContextFromViewKey] :
fromVC.view;
UIView *toView = hasViewForKey ?
[transitionContext viewForKey:UITransitionContextToViewKey] :
toVC.view;
// iOS8 has a bug where viewForKey:to is nil: http://stackoverflow.com/a/24589312/59198
// The workaround is: A) get the 'toView' from 'toVC'; B) manually add the 'toView' to the container's
// superview (eg the root window) after the completeTransition call.
BOOL toViewNilBug = !toView;
if (!toView) { // Workaround by getting it from the view.
toView = toVC.view;
}
UIView *container = [transitionContext containerView];
UIView *containerSuper = container.superview; // Used for the iOS8 bug workaround.
// Perform the transition.
toView.frame = container.bounds;
[container insertSubview:toView belowSubview:fromView];
[UIView animateWithDuration:kDuration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
fromView.frame = CGRectOffset(container.bounds, 0, CGRectGetHeight(container.bounds));
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
if (toViewNilBug) {
[containerSuper addSubview:toView];
}
}];
}
J'ai trouvé que cela fonctionnait bien pour Obj-C:
[transitionContext completeTransition:YES];
if(![[UIApplication sharedApplication].keyWindow.subviews containsObject:toViewController.view]) {
[[UIApplication sharedApplication].keyWindow addSubview:toViewController.view];
}
Semble fonctionner correctement sur iOS 7 et iOS 8.
J'ai trouvé que ce bug (et beaucoup d'autres!) Disparaissait si vous définissiez modalPresentationStyle = UIModalPresentationFullScreen
. Bien sûr, vous obtenez toujours votre animation de transition personnalisée.
Après avoir rencontré ce problème, j'étais très confus, car j'avais écrit quelque chose de presque identique il n'y a pas si longtemps qui fonctionnait bien. Je suis venu ici chercher des réponses pour trouver des solutions qui paraissent plutôt mal fagotées, et ne semblent pas comprendre la cause fondamentale ... c'est en fait très facile à corriger.
Certaines réponses mentionnent le changement de modalPresentationStyle
en .overFullScreen
. C'est correct, .overCurrentContext
fonctionnerait aussi. Ceci est attendu, et le comportement Apple documents. Mais pourquoi cela ne fonctionne-t-il pas pour tout le monde? Pourquoi tout le code hacky, et les combinaisons de ceci avec autre chose, et des trucs fous que vous ne devriez pas ne pas faire?
Il s'avère que vous devez définir le style de présentation AVANT QUE LA VUE CHARGE. Pas après. Faites-le dans init, ou faites-le depuis le contrôleur précédent, ou comme vous le souhaitez - tant que c'est avant le chargement de la vue.
L'utilisation du nouveau UIModalPresentationOverCurrentContext a corrigé le problème. Ma transition initiale sur iOS 7 consistait simplement à avoir un arrière-plan flou de la vue sous le modal.
Je suis resté bloqué sur cette question aussi. Je cherchais à créer une transition personnalisée avec un arrière-plan semi-transparent dans lequel je pouvais toujours voir le contrôleur de vue d'où je venais, mais je n'avais qu'un arrière-plan noir. J'ai trouvé que la réponse de Mark Aron dans ce fil m'avait aidé, mais comme elle est écrite en Objective C, voici une version Swift 3 de cette réponse que j'ai testée pour iOS 9 et iOS 10:
Créez une sous-classe de UIPresentationController. Remplacez le shouldRemovePresentersView par false comme suit:
class ModalPresentationController: UIPresentationController {
override var shouldRemovePresentersView: Bool {
return false
}
override func containerViewWillLayoutSubviews() {
presentedView?.frame = frameOfPresentedViewInContainerView
}
}
À l'endroit où vous instanciez le nouveau contrôleur de vue et définissez son délégué de transition, indiquez que vous souhaitez qu'il affiche un style de présentation modal personnalisé, comme suit:
let newVC = mainStoryboard.instantiateViewController(withIdentifier: "newVC") as! NewViewController
newVC.transitioningDelegate = self
newVC.modalPresentationStyle = UIModalPresentationStyle.custom
newVC.modalPresentationCapturesStatusBarAppearance = true //optional
present(newVC, animated: true, completion: nil)
Remplacez maintenant la méthode presentationController de votre UIViewControllerTransitioningDelegate et renvoyez votre UIPresentationController personnalisé. J'avais le mien comme extension de ma classe actuelle:
extension CurrentViewController: UIViewControllerTransitioningDelegate {
//this is where you implement animationController(forPresented) and animationController(forDismissed) methods
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return ModalPresentationController(presentedViewController: presented, presenting: source)
}
}
Une autre chose à noter est que vous ne devriez pas essayer de référencer votre fromView dans votre classe presentAnimator. Ce sera nul et vous obtiendrez une erreur au moment de l'exécution. En dehors de cela, si vous implémentez des éléments tels que des éléments, vous obtiendrez votre transition personnalisée avec son animation et un arrière-plan semi-transparent si vous en créez une.