J'ai un contenu UIViewController qui présente un paramètres UIViewController en utilisant une transition personnalisée. La présentation est avec presentViewController:animated:completion:
.
Lorsque, par la suite, je rejette les paramètres avec dismissViewControllerAnimated:completion:
, le contrôleur de présentation revient soudainement à sa position initiale avant la présentation du contrôleur de paramètres.
J'ai un moyen de contourner ce problème sur l'appareil mais pas sur le simulateur. Cependant, j'aimerais savoir ce que je fais mal plutôt que de pirater une bodge qui le fait disparaître. Je prévois également de rendre cette animation interactive, et je suppose que ces problèmes vont s’amplifier lorsque je le ferai.
L’effet souhaité est que le contrôleur présentant glisse vers le bas de l’écran et que le contrôleur présenté semble se trouver derrière lui, de là où il se soulève pour remplir l’écran. Le haut du contrôleur de présentation reste à l'écran pendant toute la durée d'utilisation du contrôleur présenté. Il reste en bas de l'écran, mais au-dessus du contrôleur présenté.
Vous pouvez imaginer soulever le capot sur une voiture (le contrôleur de présentation avant) pour voir le moteur derrière (les réglages présentés), mais le capot reste visible en bas pour un peu de contexte.
Je prévois d’affiner la chose pour que le contrôleur de présentation apparaisse réellement avec une perspective 3D, mais je n’en suis pas encore là.
Lorsque les paramètres sont ignorés, le contrôleur de présentation d'origine (capot) doit remonter à l'écran et le contrôleur présenté (paramètres) s'affaisse légèrement (fermeture du capot).
Voici la méthode qui bascule les paramètres sur et en dehors de l'écran (elle est simplement appelée par un bouton UIButton). Vous remarquerez que le contrôleur de la vue de présentation se configure en tant que <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];
}
}
Pour implémenter <UIViewControllerAnimatedTransitioning>
, le contrôleur de vue de présentation se retourne lui-même simplement sous le <UIViewControllerAnimatedTransitioning>
-(id<UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return self;
}
-(id<UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed
{
// Test Point 1.
return self;
}
Donc, finalement, le contrôleur de vue de présentation recevra 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;
}
Dans le code ci-dessus, j'ai indiqué !!! Cette ligne ne devrait pas être nécessaire !!! .
Ce qui se passe est qu'entre Point de test 1 et Point de test 2 , la position de l'écran du contrôleur de la vue de présentation est réinitialisée pour devenir les limites du plein écran par défaut. Ainsi, au lieu d’être au bas de l’écran, prêt à reprendre l’animation en douceur, il saute soudainement vers le haut de l’écran pour indiquer qu’il doit être animé aussi!
J'ai essayé diverses méthodes pour animer le contrôleur de vue de présentation sur l'écran:
Dans tous les cas, dans Point de test 1 , lorsque le délégué de transition est demandé, le contrôleur de présentation est configuré comme je le pensais. Cependant, dans tous les cas, dans Point de test 2 , le contrôleur de la vue présentation a perdu la position correcte et a été "effacé" pour afficher la position normale plein écran à laquelle je souhaite l'animer.
Dans le travail ci-dessus, je déplace explicitement le contrôleur de la vue de présentation là où il devrait être au début de l'animation avec !!! Cette ligne ne devrait pas être nécessaire !!! . Cela semble fonctionner sur l'appareil avec la version actuelle d'iOS 7. Toutefois, sur le simulateur, le contrôleur est visible à la position libérée pendant au moins une image.
Je soupçonne que je fais quelque chose de mal, et que je vais avoir des problèmes avec ma solution de contournement en masquant un autre problème.
Avez-vous une idée de ce qui passe? Merci!
Quelques pièges potentiels avec le renvoi des contrôleurs de vue à présentation modale à l'aide d'animations de transition personnalisées:
Compte tenu de tout cela, je pense que cela devrait fonctionner:
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = transitionContext.containerView;
const BOOL isUnwinding = [toController presentedViewController] == fromController;
const BOOL isPresenting = !isUnwinding;
UIViewController *presentingController = isPresenting ? fromController : toController;
UIViewController *presentedController = isPresenting ? toController : fromController;
[containerView addSubview:presentingController.view];
[containerView bringSubviewToFront:presentingController.view];
if(isPresenting)
{
// 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
{
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.7;
[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]];
}];
}
}
Initialement, j'ai pensé à utiliser CATransition
pour avoir un effet de transition personnalisé lorsque presentViewController:animated:completion:
et dismissViewControllerAnimated:completion:
étaient un contrôleur de vue. Mais vous souhaitez afficher une partie de View Controller lorsque le paramètre View Controller est présenté, alors je pense que CATransition
n’aiderait pas, car vous n’avez pas le contrôle total sur la distance à laquelle vous souhaitez déplacer le View Controller.
Je pense que le moyen le plus simple est d’avoir un seul contrôleur de vue avec deux UIView en plein écran. Pour la première UIView (la vue du contrôleur de vue, c'est-à-dire, self.view), vous mettez en page le paramètre et sur la deuxième UIView, il s'agit de la vue normale. Dans ViewDidLoad, vous ajoutez la deuxième vue à l'aide de [self.view addSubview:2ndView];
. Plus tard, lorsque vous souhaitez présenter la vue des paramètres, vous pouvez faire
CGRect frame = secondView.frame;
frame.Origin.y = the_y_coordinate_you_like;
UIView animateWithDuration:0.2 animations:^{
secondView.frame = frame;
}];
ensuite, faites l’autre façon de ramener la 2ndView.