UIViewController - problema com a transição de dispensa personalizada

Sumário

Eu tenho um UIViewController de conteúdo que apresenta um UIViewController de configurações usando uma transição personalizada. A apresentação é compresentViewController:animated:completion:.

Mais tarde, quando descartar as configurações comdismissViewControllerAnimated:completion:, o controlador de apresentação é repentinamente retornado à sua posição inicial antes da apresentação do controlador de configurações.

Eu tenho uma solução alternativa para isso no dispositivo, mas não no simulador. No entanto, eu gostaria de saber o que estou fazendo de errado, em vez de invadir uma loja que a faz desaparecer. Também pretendo tornar essa animação interativa e suspeito que esses problemas serão ampliados quando eu fizer isso.

Transição personalizada - Abrindo o capô

O efeito desejado é que oapresentando o controlador desliza para baixo na tela e oapresentado O controlador é visto atrás dele, de onde levanta para preencher a tela. A parte superior do controlador de apresentação permanece na tela durante a vida útil do uso do controlador apresentado. Ele fica na parte inferior da tela, masacima o controlador apresentado.

Você pode imaginar levantando o capô de um carro (o controlador de apresentação frontal) para ver o motor por trás (as configurações apresentadas), mas o capô permanece visível na parte inferior por um pouco de contexto.

Pretendo refinar isso para que o controlador de apresentação pareça realmente elevar a perspectiva de uma maneira 3D, mas ainda não cheguei tão longe.

Quando as configurações são descartadas, o controlador de apresentação original (castelo) deve deslizar para cima na tela e o controlador apresentado (configurações) afunda um pouco (fechando o castelo).

Código

Aqui está o método que ativa e desativa as configurações da tela (é chamado apenas por um UIButton). Você notará que o controlador de exibição de apresentação se configura como o<UIViewControllerTransitioningDelegate>.

-(void) toggleSettingsViewController
{
  const BOOL settingsAreShowing = [self presentedViewController] != nil;
  if(!settingsAreShowing)
  {
    UIViewController *const settingsController = [[self storyboard] instantiateViewControllerWithIdentifier: @"STSettingsViewController"];
    [settingsController setTransitioningDelegate: self];
    [settingsController setModalPresentationStyle: UIModalPresentationCustom];
    [self presentViewController: settingsController animated: YES completion: nil];
  }
  else
  {
    [self dismissViewControllerAnimated: YES completion: nil];
  }
}

Implementar<UIViewControllerAnimatedTransitioning> o controlador de exibição de apresentação também apenas retorna como o<UIViewControllerAnimatedTransitioning>

-(id<UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
  return self;
}

-(id<UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed
{
  // Test Point 1.
  return self;
}

Então, finalmente, o controlador de exibição de apresentação receberáanimateTransition::

-(void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
  UIViewController *const fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  UIViewController *const toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

  const BOOL isUnwinding = [toController presentedViewController] == fromController;
  const BOOL isPresenting = !isUnwinding;

  UIViewController * presentingController = isPresenting ? fromController : toController;
  UIViewController * presentedController = isPresenting ? toController : fromController;

  if(isPresenting)
  {
    // Add the presented controller (settings) to the view hierarchy _behind_ the presenting controller.
    [[transitionContext containerView] insertSubview: [presentedController view] belowSubview: [presentingController view]];

    // Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
    presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
    presentedController.view.alpha = 0.7;

    [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
      // Lift up the presented controller.
      presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);

      // Brighten the presented controller (out of shadow).
      presentedController.view.alpha = 1;

      // Push the presenting controller down the screen – 3d effect to be added later.
      presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
     } completion: ^(BOOL finished){
       [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
     }];
  }
  else
  {
    // Test Point 2.

    // !!!This line should not be needed!!!
    // It resets the presenting controller to where it ought to be anyway.
    presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);

    [UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
      // Bring the presenting controller back to its original position.
      presentingController.view.layer.transform = CATransform3DIdentity;

      // Lower the presented controller again and put it back in to shade.
      presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
      presentedController.view.alpha = 0.4;
    } completion:^(BOOL finished) {
      [transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
    }];
  }
}

-(NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
  return 0.5;
}
Problema

No código acima, eu indiquei!!! Esta linha não deve ser necessária !!!.

O que está acontecendo é que entrePonto de teste 1 ePonto de teste 2 a posição da tela do controlador de exibição de apresentação é redefinida para ser os limites de tela cheia padrão. Então, em vez de estar na parte inferior da tela pronta para animar novamente sem problemas, ela subitamente salta a tela para a posição de que também deve animar!

Eu tentei várias abordagens para animar o controlador de exibição de apresentação na tela:

Eu mudei o quadro de vista.Eu mudei a transformação de sua visão.Alterei a transformação 3d da camada da visualização.

Em todos os casos, emPonto de teste 1, quando o delegado de transição é solicitado, o controlador de apresentação é configurado como eu esperaria. No entanto, em todos os casos, emPonto de teste 2, o controlador de exibição de apresentação perdeu a posição correta e foi "limpo" para ter a posição normal de tela cheia na qual desejo animar.

No trabalho acima, mudei explicitamente o controlador de exibição de apresentação para onde deveria estar no início da animação com!!! Esta linha não deve ser necessária !!!. Isso parece funcionar no dispositivo com a versão atual do iOS 7. No entanto, no simulador, o controlador fica visível na posição limpa por pelo menos um quadro.

Desconfio que estou fazendo outra coisa errada e que vou ter problemas com minha solução alternativa, mascarando outro problema.

Alguma ideia sobre o que se passa? Obrigado!

questionAnswers(2)

yourAnswerToTheQuestion