«From View Controller» disparaît à l'aide de UIViewControllerContextTransitioning

105

J'ai un problème et je l'ai décrit ci-dessous.

J'utilise UIViewControllerContextTransitioningpour des transitions personnalisées.

J'ai 2 contrôleurs de vue, un premier contrôleur de vue et un deuxième contrôleur de vue.

Maintenant, je veux ajouter un deuxième contrôleur de vue sur le premier contrôleur de vue avec une animation. Je l'ai atteint, maintenant le deuxième contrôleur de vue est transparent, nous pouvons donc voir le premier contrôleur de vue sous le deuxième contrôleur de vue.

Mais je ne peux pas voir le premier contrôleur de vue, et je ne peux voir que l'écran noir sous le deuxième contrôleur de vue.

-(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];
    }];
}

Lorsqu'il est [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.

NiravPatel
la source

Réponses:

98

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 devenu noir. La clé UIWindowest complètement vide - aucune hiérarchie de vue!

Révélé

J'ai joué un peu et il semble qu'il existe une solution de contournement simple, pour les cas simples. Vous pouvez simplement rajouter la toViewControllervue de la sous-vue de la fenêtre clé:

transitionContext.completeTransition(true)
UIApplication.sharedApplication().keyWindow!.addSubview(toViewController.view)

J'ai vérifié et la fenêtre de clé rootViewControllerest toujours correctement définie, donc c'est très bien. Je ne suis pas sûr de ce qui se passerait si vous présentiez votre contrôleur à partir d'un contrôleur modal déjà présenté, donc pour les cas plus complexes, vous devrez expérimenter.

Sillon de cendre
la source
2
J'ai également vu ce problème. iOS 8 introduit une nouvelle méthode et des clés pour accéder à fromView et toView (Remarque: pas de contrôleur de vue) Il semble que ces références ne soient pas perdues pendant la transition. Vous pouvez les ajouter à la vue du conteneur comme vous le feriez normalement si vous veniez de les récupérer à partir des contrôleurs de vue.
tapi
1
Je voyais une bizarrerie similaire sur iOS 8 en essayant d'ajouter des sous-vues à la vue de mon contrôleur de navigation, dans viewDidLoad. Le réajout de la vue navigationController à la keyWindow a semblé faire l'affaire, merci beaucoup, Ash!
taber
1
Je vois toujours cela dans GM (et ce correctif fonctionne toujours). Les autres voient-ils la même chose? Est-ce juste un changement dans l'API?
rjkaplan
21
J'ai trouvé que ce bogue (et bien d'autres!) Disparaît si vous définissez modalPresentationStyle = UIModalPresentationFullScreen. Bien sûr, vous obtenez toujours votre animation de transition personnalisée.
Chris
1
Merci @AshFurrow. Belle solution de contournement jusqu'à ce qu'elle soit corrigée!
kandelvijaya
78

Je pense que le raisonnement derrière cela devrait être mieux expliqué.

La vue disparaît car vous retirez la vue du contrôleur de vue de présentation de son emplacement d'origine (hiérarchie de vues), placez-la dans le containerView que votre animateur fournit mais ne la retourne jamais une fois l'animation terminée. Ainsi, la vue du contrôleur de vue est complètement supprimée avec sa vue supervisée (containerView) de la fenêtre.

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) à leur emplacement d'origine une fois l'animation terminée 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 vue de présentation que si la vue de ce contrôleur de vue va être complètement masquée (supprimée de la hiérarchie de vue) à la fin de la 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 la vue du contrôleur de vue de présentation. Par exemple, si vous définissez l'opacité de la vue du contrôleur de vue présenté à 50% et utilisez UIModalPresentationFullScreen, vous ne pourrez pas voir la vue du contrôleur de vue présentée sous le présenté, mais si vous utilisez UIModalPresentationOverFullscreen - vous (la shouldRemovePresentersViewméthode d'UIPresentationController est responsable de le spécifier).

Pourquoi ne pas permettre à l'animateur de manipuler la vue du contrôleur de vue de présentation à tout moment? Tout d'abord, si la vue du contrôleur de vue de 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 est. Deuxièmement, si la propriété de ce contrôleur de vue est transférée au 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 lorsque cela est nécessaire, par exemple lorsque l'orientation change, mais le propriétaire d'origine du contrôleur de vue de présentation le fait. .

Dans iOS 8, la viewForKey:méthode a été introduite pour obtenir des vues que l'animateur manipule. Tout d'abord, il est utile de suivre la règle décrite ci-dessus en renvoyant nul chaque fois que l'animateur ne doit pas toucher la vue. Deuxièmement, il peut renvoyer une vue différente pour l'animateur à animer. Imaginez que vous implémentez une présentation similaire à une feuille de formulaire. Dans ce cas, vous voudrez 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ée 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 faire aucune hypothèse sur les vues dont il a besoin pour animer.

Il y a plusieurs choses que vous pouvez faire pour résoudre correctement un problème avec la vue d'un contrôleur de vue de présentation qui disparaît lorsque vous le placez explicitement dans la vue conteneur de l'animateur:

  1. Si vous n'avez pas besoin d'animer la vue du contrôleur de vue de présentation, utilisez viewForKey:pour obtenir des vues à animer au lieu d'atteindre directement les vues du contrôleur. viewForKey:peut renvoyer des vues nulles ou même complètement différentes.

  2. Si vous souhaitez animer la vue des contrôleurs de vue de présentation, vous devez envisager d'utiliser le UIModalPresentationFullScreenstyle ou continuer à utiliser UIModalPresentationCustomet à implémenter votre propre sous-classe de UIPresentationController avec shouldRemovePresentersViewretour YES. En fait, la mise en œuvre de cette méthode est la principale différence entre les contrôleurs de présentation internes définis par UIModalPresentationFullScreenet les UIModalPresentationCustomstyles mis à part le fait que ce dernier vous permet d'utiliser des contrôleurs de présentation personnalisés.

  3. Dans tous les autres cas rares, vous devrez ramener la vue du contrôleur de vue de présentation à son emplacement d'origine, comme d'autres réponses le suggèrent.

egdmitry
la source
2
C'est super bizarre parce que ce code ne repose sur viewControllerForKey:les s viewque lorsque le viewForKey:retour est nul, et je devais encore le rajouter manuellement à la fenêtre. Avez-vous un exemple de code fonctionnant sans cette solution de contournement?
Ash Sillon
Eh bien, si viewForKey:retourne nil, vous devrez certainement ajouter à nouveau la vue du contrôleur de vue de présentation à la fenêtre si vous la supprimez dans votre animateur. Dans le cas où viewForKey renvoie la vue réelle du contrôleur de vue, il est prudent de déplacer cette vue car UIKit la ramènerait à sa position d'origine une fois le cycle de vie de la présentation terminé.
egdmitry
Merci d'avoir expliqué le raisonnement derrière ce problème. Vous avez absolument raison. Déplacer la position de la vue dans la hiérarchie des vues sans la remplacer la ferait évidemment disparaître (après iOS 8, et je travaille avec iOS 10 en ce moment!) Merci d'avoir clarifié.
Clay Ellis
1
Merci egdmitry pour votre clarification. Ce qui soulève une autre question qui est: comment pensez-vous que je devrais mettre en œuvre une présentation comme une révélation ? Un de ces très courants de nos jours où la vue de présentation glisse partiellement pour montrer la vue présentée en dessous? Dans ce scénario, les vues de présentation et présentées doivent être à l'écran et la vue de présentation est celle animée.
Andrea
70

Dans iOS 8, vous devez manipuler les vues renvoyées par viewForKey:au lieu de la .viewpropriété des contrôleurs de vue renvoyés par viewControllerForKey:. Ce n'est pas particulièrement clair dans la documentation bêta, mais si vous regardez dans la source de UIViewControllerTransitioning.h, vous verrez ce commentaire ci-dessusviewControllerForKey: - :

// 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 d'ajuster les images, etc. 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 toViewControlleret fromViewControllercomme d'habitude, mais obtenez les vues en utilisant [toViewController viewForTransitionContext:transitionContext].

Modifier: il semble y avoir un bogue, où la vue du contrôleur de la vue de présentation est nulle lorsqu'elle est renvoyée viewForKey, ce qui vous empêche d'effectuer des transitions modales qui animent la vue de présentation (comme un glissement ou un basculement horizontal). J'ai déposé un bogue pour iOS8 sur rdar: // 17961976 ( http://openradar.appspot.com/radar?id=5210815787433984 ). Consultez également l'exemple de projet à l' adresse http://github.com/bcherry/TransitionBug

Edit 2: Merci à Graveley pour la suggestion, l'utilisation de UIModalPresentationFullScreen résout le problème. Ce n'est peut-être pas un bug. Apple peut avoir l'intention que UIModalPresentationCustom modifie uniquement la vue du modal entrant. Si vous souhaitez modifier la vue sortante, vous devez garantir une présentation plein écran de la nouvelle vue? Dans tous les cas, vous devez utiliser viewForKeyet UIModalPresentationFullScreen.

bcherry
la source
2
Le bogue viewForKey me rendait fou! - merci pour le dépôt. FWIW ma transition fonctionne correctement en obtenant la vue de UITransitionContextToViewControllerKey, mais ma transition n'applique qu'une transformation à la vue entière. Je ne sais pas si cela doit être interprété comme manipulatingles vues des VC ou non ...
MathewS
1
Wow - c'est fou. Je n'ai pas vu cela dans les différences - probablement parce que ce n'est qu'un petit commentaire. Vraiment frustrant quand Apple fait une cascade comme celle-ci. Croisons les doigts sur votre radar.
Ash Sillon
Je vois aussi le viewForKeybogue dans le GM. Les autres aussi? Avez-vous trouvé une solution de contournement raisonnable pour cela?
rjkaplan le
2
J'ai pensé selon le commentaire de - viewForKey// viewForKey: peut retourner nil, ce qui indiquerait que l'animateur ne devrait pas manipuler la vue du contrôleur de vue associé. Le retour niln'est pas un bug.
Ken Kuan le
4
@kenKuan vous avez peut-être raison. lors de l'utilisation de UIModalPresentationFullScreen, viewForKeyrenvoie la vue de et la vue à. Alors peut-être que c'est intentionnel qu'il renvoie nul pour UIModalPresentationCustom. Je mets à jour mon rapport de bogue et je le publierai ici si j'en reviens d'Apple.
bcherry
24

Pas de réglage modalPresentationStyle UIModalPresentationCustom a résolu 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 en voie de disparition. Notez que le protocole UIViewControllerTransitioningDelegate semble toujours être suivi même en le laissant à la valeur par défaut. Si je me souviens bien, il était une fois UIModalPresentationCustom était une exigence.

Fonctionne jusqu'à présent, je n'ai essayé cela que pour les animations non interactives.

graveley
la source
1
sensationnel. Cela l'a fait! J'ai testé sans modalPresentationStyle dans iOS7 et 8 et cela fonctionne dans les deux. Merci!!
Ah Ryun Moon
1
Je vous remercie! Ceci combiné avec l'utilisation viewForKey:au lieu de .viewsur les viewControllerForKey:corrige tous les problèmes pour moi.
bcherry
1
Cela a résolu le problème pour moi sans utiliser viewForKey, mais je suppose que cela devrait également être utilisé.
Kevin Sliech
5
Bien que cela semble résoudre le problème, il est important de noter que l'écran derrière votre contrôleur de vue deviendra noir une fois qu'il sera affiché. Ceci est important si votre contrôleur de vue n'est pas en plein écran.
Le mec
16

J'ai trouvé cette réponse extrêmement utile dans un fil de discussion connexe de Lefteris: https://stackoverflow.com/a/27165723/3709173

Résumer:

  1. définir modalPresentationStyle sur .Custom
  2. sous-classe UIPresentationController, remplacer shouldRemovePresentersView (sans NO)
  3. remplacer presentationControllerForPresentedViewController dans votre classe TransitionDelegate et renvoyer votre UIPresentationController personnalisé

+1 dans votre transition personnalisée, n'ajoutez pas toView lorsque l'animation de renvoi se produit.

Démontré ici:

https://www.dropbox.com/s/7rpkyamv9k9j18v/CustomModalTransition.zip?dl=0 sans aucun hacks! c'est comme de la magie! :)

Mark Aron Szulyovszky
la source
1
C'est la vraie bonne réponse. Sans tours de magie comme dans celui accepté. Merci, Mark!
Andrei Malygin
Malheureusement, cela ne fonctionne pas sous iOS 12.4, Xcode 10.3. L'écran devient noir une fois la transition terminée (toutes les vues ont été supprimées de la hiérarchie. Cependant, définir la propriété 'modalPresentationStyle' sur '.fullscreen' fonctionne. Bravo.
Womble
J'ai essayé la version Obj-C de l'implémentation Swift de Mark et gwinyai dans mon projet. Malheureusement, aucun d'entre eux ne fonctionne comme prévu. J'utilise Xcode 11.1 et la cible de construction est iOS 13.0, j'ai essayé à la fois sur l'appareil et sur le simulateur. Dans mon cas, ma configuration de base est une vue de collection et lorsque vous appuyez sur une cellule, elle passe à une vue de détail avec animation. Cependant, cela fonctionne parfaitement si j'utilise l'animation de transition par défaut. Le VC de présentation ne sera pas parti lorsque je reprendrai les détails de la vue.
infinity_coding7
8

Dans iOS 8, vous devez créer un UIPresentationController et implémenter la méthode ci-dessous, dans le UIViewControllerTransitioningDelegate.

- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source;

Demande à votre délégué le contrôleur de présentation personnalisé à utiliser pour gérer la hiérarchie des vues lors de la présentation d'un contrôleur de vue.

Valeur de retour:

Le contrôleur de présentation personnalisé pour 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 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 votre 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 aucun contenu à la hiérarchie des vues.

Disponibilité Disponible dans iOS 8.0 et versions ultérieures.

Pour plus d'informations, regardez la vidéo WWDC 2014:

https://developer.apple.com/videos/wwdc/2014/?include=228

Il existe également un exemple de code de la WWDC appelé «LookInside: Adaptivité des contrôleurs de présentation et objets d'animation personnalisés», que vous pouvez télécharger à partir de la page de codes d'exemple de la WWDC 2014.

Vous devrez peut-être modifier un peu l'exemple de code. La méthode d'initialisation UIPresentationController a été modifiée en:

initWithPresentedViewController:presented presentingViewController:presenting

Avant, il était présenté puis présenté. Échangez-les et cela devrait fonctionner.

Paulo Faria
la source
Désolé de ne pas avoir regardé la vidéo liée, mais je ne pense pas que vous ayez besoin d'un UIPresentationController personnalisé à moins que vous ne souhaitiez une présentation non standard une fois l'animation terminée, comme une vue circulaire présentée. Si vous souhaitez simplement une animation différente, l'implémentation de UIViewControllerAnimatedTransitioning devrait suffire, d'après mes connaissances limitées.
Vaddadi Kartick
7

au lieu de [inView insertSubview: toViewController.view aboveSubview: fromViewController.view]; il suffit d'ajouter: [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

CarlosGz
la source
Cela devrait être la réponse acceptée pour faire une animation de type UIModalPresentationStyleCustom car il n'est pas nécessaire d'ajouter le fromViewController au containerView. Il vous suffit d'ajouter le toViewController pendant l'animation de présentation.
Scott Kaiser
C'est très utile, en fait
Dmitry Bondarev
7

Voici une version Objective C du correctif d'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]

J'ai dû échanger la commande et appeler la méthode [transitionContext completeTransition:] après avoir rajouté la vue pour obtenir la présentation d'un nouveau contrôleur de vue du bloc d'achèvement de renvoi d'un autre contrôleur de vue pour 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é!

vichudson1
la source
5

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 ios7 et ios8.

De roseaux
la source
5

Je l'ai trouvé viewForKey:UITransitionContextToViewKey renvoie nul sur ios8. Donc, si c'est nul, je saisis la vue du contrôleur de vue 'to'.

Cependant, cela semble avoir pour conséquence que la vue «à» ne soit pas déplacée du conteneur vers la fenêtre lorsqu'elle completeTransition:YESest appelée. Donc si viewForKey:UITransitionContextToViewKeyretourne nul, je tombetoVC.view , et garde une trace du fait qu'il a renvoyé nil, et après l'achèvement, je le déplace vers la supervision initiale du conteneur (qui se trouve être la fenêtre).

Donc, ce code fonctionne aussi bien sur iOS7 que sur iOS8, et devrait également fonctionner 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];
        }
    }];
}
Chris
la source
3

J'ai trouvé que ce bogue (et bien d'autres!) Disparaît si vous définissez modalPresentationStyle = UIModalPresentationFullScreen. Bien sûr, vous obtenez toujours votre animation de transition personnalisée.

Chris
la source
2

Je suis resté coincé sur ce problème aussi. Je cherchais à créer une transition personnalisée avec un arrière-plan semi-transparent où 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é la réponse de Mark Aron dans ce fil de discussion qui m'a aidé mais elle est écrite en Objective C, voici donc une version Swift 3 de cette réponse que j'ai testée pour iOS 9 et iOS 10:

  1. Créez une sous-classe de UIPresentationController. Remplacez shouldRemovePresentersView par false comme suit:

    class ModalPresentationController: UIPresentationController {
    
    override var shouldRemovePresentersView: Bool {
    return false
    }
    
    override func containerViewWillLayoutSubviews() {
    presentedView?.frame = frameOfPresentedViewInContainerView
    }
    }
  2. À 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)
  3. 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 devez pas essayer de référencer votre fromView dans votre classe presentAnimator. Ce sera nul et vous obtiendrez une erreur lors de l'exécution. À part cela, si vous implémentez des choses comme des choses, vous obtiendrez votre transition personnalisée avec son animation et un arrière-plan semi-transparent si vous en faites un.

gwinyai
la source
Ceci est un excellent exemple pour faire une présentation modale personnalisée dans Swift 3! Merci @gwinyai! Je suis resté super coincé jusqu'à ce que je trouve un exemple qui montrait la nouvelle API swift 3 presentationController(forPresented presented UIViewController,... car la précédente API swift n'a pas dérangé le complicateur mais n'a pas été appelée.
Natalia
2

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 à la recherche de réponses pour trouver des correctifs qui semblent assez piratés et qui ne semblent pas comprendre la cause profonde ... c'est en fait très facile à corriger.

Certaines réponses mentionnent le passage modalPresentationStyleà .overFullScreen. C'est correct, .overCurrentContextcela fonctionnerait aussi. Cela est attendu, et le comportement des documents Apple. Mais pourquoi cela ne fonctionne-t-il pas pour tout le monde? Pourquoi tout le code hacky, et les combinaisons de cela avec autre chose, et des trucs fous que vous ne devriez pas faire?

Il s'avère que vous devez définir le style de présentation AVANT LES CHARGES DE VUE . Pas après. Faites-le dans init, ou faites-le à partir du contrôleur précédent, ou comme vous le souhaitez - tant que c'est avant le chargement de la vue.

Jordan Smith
la source
1
J'ai défini le style de présentation .overCurrentContext avant le chargement de la vue (dans le initcontrôleur de vue) et le problème se produit toujours
Ricardopereira
1

L'utilisation du nouveau UIModalPresentationOverCurrentContext l'a corrigé pour moi. Ma transition originale sur iOS 7 consistait simplement à avoir un arrière-plan flou de la vue sous le modal.

malhal
la source
Pour une raison quelconque, cela ne semble pas permettre une interaction avec la vue en dessous, là où UIModalPresentationCurrentContext l'a fait dans iOS 7 .. Des pensées?
Christopher Wirt
Hmm pour moi sur iOS 10, .overCurrentContext entraîne ce bogue mais pas .fullscreen. Je suis venu ici dans l'espoir d'un correctif pour l'utilisation de .overCurrentContext, mais jusqu'à présent, rien ne semble fonctionner dans iOS 10, sauf peut-être en sous-classant UIPresentationController ...
Natalia
0

Ok, les gars, je pense que j'ai résolu un cas où `` un animateur qui travaille '' cesse de fonctionner correctement lorsque vous créez une application sous iOS 13 et supérieur.

Env Xcode 11.1, iOS 13.1

Problème

Ce que je veux faire est très simple: j'ai une vue de collection, quand une cellule est tapée, elle passe à une vue de détail. Au lieu d'utiliser le style par défaut ennuyeux de «présenter modalement», je veux le rendre plus intéressant, j'ai donc écrit un animateur pour la transition du contrôleur de vue.

J'ai mis en place le segue dans IB par glisser-déposer de ma collection VC au détail VC. Le style de segue est «Présent modalement» et la présentation est définie sur «Plein écran».

Lorsqu'il affiche la vue détaillée, tout fonctionne comme prévu. Cependant, lorsque je ferme la vue détaillée et retourne à la vue de la collection, je ne peux voir que la vue de détail animée, la vue de la collection a simplement disparu. J'ai poussé ici et là et j'ai quelques découvertes

Juste après l'appel de la ligne suivante à partir de la fonction 'animateTransition ()', la vue de la collection reprend et apparaît

transitionContext.completeTransition(true)

Tant que la vue détaillée ne couvre pas entièrement la vue de la collection, la vue de la collection ne disparaîtra pas lorsqu'elle reviendra de la vue de détail

Solution

Pour être honnête, je sais peu de choses sur le fonctionnement de la transition animée. Donc je ne peux que suivre ce post et l'autre , essayer chacune des réponses. Malheureusement, aucun d'entre eux ne fonctionne pour moi. Enfin, je suis arrivé à un point où la seule chose que je puisse modifier est le style de présentation de segue dans IB (ce que j'aurais dû faire au tout début). Lorsque je règle la présentation sur «Sur plein écran», un miracle se produit et mon problème est résolu. La vue détaillée peut s'afficher en plein écran avec une animation et lorsqu'elle est rejetée, je peux voir à la fois la vue de la collection en arrière-plan et la vue de détail animée.

Puis une autre découverte le long de la route

Pour faire référence à 'toView' et 'fromView', les deux méthodes suivantes fonctionnent

Indirectement façon:

transitionContext.viewController(forKey: .to)?.view
transitionContext.viewController(forKey: .from)?.view

Directement façon:

transitionContext.view(forKey: .to)
transitionContext.view(forKey: .from)

Mais lorsque j'ai basculé le style de segue sur `` Over Full Screen '', la manière directe retourne `` nil '' pour `` toView '' et `` fromView '' et fonctionne uniquement indirectement, ce problème est également mentionné dans un autre article , donc je pense que cela vaut la peine pour poster ma petite découverte ici.

J'espère que cela sera utile à quelqu'un à l'avenir.

infinity_coding7
la source
0

J'avais le même problème lors du rejet d'un contrôleur de vue de contenu.

Mon application a ce contrôleur de vue parent montrant un contrôleur de vue enfant (présentant vc) de manière modale. Ensuite, lorsqu'une sous-vue dans le childVC est tapée, elle montre un autre vc (que j'appelle le contrôleur de vue de contenu (présenté vc))

Mon problème est que, lors de la suppression du contentVC (maintenant le vc de présentation), il devrait aller au VC enfant (maintenant le VC présenté) mais dès que ma transition personnalisée se termine, childVC disparaît soudainement, montrant le VC parent.

Ce que j'ai fait pour résoudre ce problème est de

  1. changer la .modalPresentationStylevaleur par défaut du childVC présenté par parentVC .automaticen.fullscreen .
  2. Puis changé le .modalPresentationStylede contentVC en .fullscreenaussi.

Cela résout le problème. mais il ne montrera pas votre VC enfant comme une feuille de style carte au-dessus de parentVC (lors de l'utilisation.overCurrentContext ou automatique) qui est nouveau dans iOS 13.

J'adorerais savoir s'il existe une solution qui conservera la feuille de style carte pour l'enfantVC lorsqu'elle est présentée par le parent.

arvinq
la source
-3

ajoute un contrôleur de vue en tant qu'enfant d'un autre contrôleur de vue.

[self addChildViewController:childViewController];                 

vérifier et laissez-moi savoir.

Rushabh
la source
je ne reçois pas, pouvez-vous le décrire en utilisant le codage?
NiravPatel
consultez cette documentation Apple developer.apple.com/library/ios/featuredarticles/…
Rushabh
cela ne répond en aucun cas à la question. Les ChildViewControllers ne sont impliqués dans aucune partie des transitions personnalisées, ils sont un sujet complètement différent.
Andras M.