web-dev-qa-db-fra.com

Implémenter une animation personnalisée pour présenter la vue modale à partir de la vue spécifiée sur iPad

Sur l'iPad, nous disposons de beaucoup plus d'espace pour travailler. Il n'est donc pas idéal de présenter des vues modales en plein écran.

Je sais comment présenter les vues modales dans le nouveau formulaire et une approche rapprochée peut être trouvée sur cette question: iPad iTunes Animation

Le problème est que vous ne pouvez pas choisir la provenance de l’animation. Il suffit donc de choisir les valeurs par défaut et de les afficher au centre. Je souhaite la personnaliser afin qu’elle apparaisse à partir d’un emplacement spécifique.

Le meilleur exemple que je puisse trouver pour cette animation est visible dans les premières secondes de cette vidéo

Si quelqu'un pouvait m'indiquer la bonne direction à l'aide de code, de tutoriels ou de documentation, je l'apprécierais beaucoup!

Mettre à jour:

Après quelques recherches, j'ai trouvé que cela pouvait être fait en utilisant des calques et Core Animation pour la première partie; et ensuite animer une vue modale formSheet mais je ne comprends toujours pas très bien comment y parvenir, espérons que vous pourrez aider!

25
Zebs

Ce que j'ai fait était de créer une nouvelle catégorie pour UIViewController comme suit

UIViewController + ShowModalFromView.h

#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>

@interface UIViewController (ShowModalFromView)

- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view;

@end

UIViewController + ShowModalFromView.m

#import "UIViewController+ShowModalFromView.h"

@implementation UIViewController (ShowModalFromView)

- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view
{
    modalViewController.modalPresentationStyle = UIModalPresentationFormSheet;

    // Add the modal viewController but don't animate it. We will handle the animation manually
    [self presentModalViewController:modalViewController animated:NO];

    // Remove the shadow. It causes weird artifacts while animating the view.
    CGColorRef originalShadowColor = modalViewController.view.superview.layer.shadowColor;
    modalViewController.view.superview.layer.shadowColor = [[UIColor clearColor] CGColor];

    // Save the original size of the viewController's view    
    CGRect originalFrame = modalViewController.view.superview.frame;

    // Set the frame to the one of the view we want to animate from
    modalViewController.view.superview.frame = view.frame;

    // Begin animation
    [UIView animateWithDuration:1.0f
                     animations:^{
                         // Set the original frame back
                         modalViewController.view.superview.frame = originalFrame;
                     }
                     completion:^(BOOL finished) {
                         // Set the original shadow color back after the animation has finished
                         modalViewController.view.superview.layer.shadowColor = originalShadowColor;
                     }];
}

@end

C'est assez simple. S'il vous plaît, faites-moi savoir si ceci vous aide.

METTRE &AGRAVE; JOUR

J'ai mis à jour la réponse pour utiliser des blocs d'animation au lieu de la paire [UIView beginAnimations:nil context:nil];/[UIView commitAnimations].

21
Mihai Fratu

On dirait que vous êtes essentiellement après avoir traduit (déplacé) une CALayer tout en la réduisant et en la faisant pivoter autour de l'axe des y simultanément. Essaye ça:

NSValue *initialTransformValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
CATransform3D translation = CATransform3DMakeTranslation(finalPoint.x, finalPoint.y, 0.0);
CATransform3D scalingAndTranslation = CATransform3DScale(translation, kMyScalingFactor, kMyScalingFactor, 1.0);
CATransform3D finalTransform = CATransform3DRotate(scalingAndTranslation, myRotationAngle, 0.0, 1.0, 0.0);
NSArray *keyFrameValues = [NSArray arrayWithObjects:initialTransformValue, [NSValue valueWithCATransform3D:finalTransform], nil];
CAKeyframeAnimation *myAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
myAnimation.values = keyFrameValues;
myAnimation.duration = kMyAnimationDuration;
myAnimation.delegate = self;
myAnimation.removedOnCompletion = NO;
myAnimation.fillMode = kCAFillModeForwards;
[myLayer addAnimation:myAnimation forKey:@"myAnimationKey"];
  • finalPoint devrait être un CGPoint dans l’espace de coordonnées de myLayer.
  • kMyScalingFactor doit être <1.0 pour réduire et> 1.0 pour augmenter.
  • myRotationAngle devrait être en radians. Utilisez des valeurs positives pour la rotation dans le sens horaire et des valeurs négatives pour la rotation dans le sens inverse.

Vous devez également implémenter un gestionnaire de terminaison d'animation pour que l'animation "reste":

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
    myLayer.transform = finalTransform;
    myLayer removeAnimationForKey:@"myAnimationKey"];
}

J'espère que cela t'aides.

5
Spiros

Je travaillais auparavant en animant simplement des vues.

1) La pochette de l'album est dans une grille.
2) Transformez la vue de la pochette de l'album en utilisant l'animation Flip.
3) Animez la vue qui se déplace sur l'écran.

J'ai rapidement jeté cela ensemble. En supposant que vous ayez un contrôleur de vue vide et 3 vues.

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self performSelector:@selector(transition) withObject:nil afterDelay:1];
    albumArtworkSquare = [[UIView alloc] initWithFrame:CGRectMake(400, 500, 300, 300)];
    albumArtworkSquare.backgroundColor = [UIColor blackColor];
    [self.view addSubview:albumArtworkSquare];

    frontViewOfAlbumArtwork = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
    frontViewOfAlbumArtwork.backgroundColor = [UIColor blueColor];
    [albumArtworkSquare addSubview:frontViewOfAlbumArtwork];

    backViewToTransitionTo = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
    backViewToTransitionTo.backgroundColor = [UIColor grayColor];
}

- (void)transition
{

    [UIView animateWithDuration:2 animations:^{
        albumArtworkSquare.frame = CGRectMake(10, 500, 300, 300);
    }];


    [UIView transitionFromView:frontViewOfAlbumArtwork toView:backViewToTransitionTo duration:2 options:UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished)
     {

         [frontViewOfAlbumArtwork removeFromSuperview];
         [albumArtworkSquare addSubview:backViewToTransitionTo];

     }];

}
2
Brandon Schlenker

La réponse de Mihai ne gère pas le retournement, contrairement à l'animation iTunes sur iPad. Je l'ai changé un peu pour retourner la vue. Vous n'avez pas besoin de toutes sortes de choses CAAnimation déjantées, mais de quelques fonctions d'animation UIView intégrées.

#import <QuartzCore/QuartzCore.h>
@interface UIViewController (ShowModalFromView)

- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view;

@end
@implementation UIViewController (ShowModalFromView)
- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view
{
    NSTimeInterval scaleSpeed = 0.3;
    NSTimeInterval flipSpeed = 0.4;

    UIView __weak *containerView = view.superview;

    view.autoresizesSubviews = YES;

    [self presentModalViewController:modalViewController animated:NO];

    UIView __weak *presentedView = modalViewController.view.superview;

    //intead of show the actual view of modalViewController, we are showing the snapshot of it to avoid layout problem
    UIGraphicsBeginImageContext(presentedView.bounds.size);
    [presentedView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage* modalSnapshot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIView __weak *originalSuperView = presentedView.superview;   


    CGRect originalFrame = presentedView.frame;
    CGRect frameInContainer = [containerView convertRect:originalFrame fromView:originalSuperView];
    [presentedView removeFromSuperview];
    UIImageView* snapshotView = [[UIImageView alloc] initWithImage:modalSnapshot];
    snapshotView.autoresizingMask = UIViewAutoresizingNone;


    [containerView bringSubviewToFront:view];

    [UIView animateWithDuration:scaleSpeed delay:0 options:UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionBeginFromCurrentState animations: ^{
        [UIView animateWithDuration:scaleSpeed
                              delay:0
                            options:UIViewAnimationOptionBeginFromCurrentState
                         animations: ^{
                             view.frame = frameInContainer;

                         }

                         completion:nil
         ];


    } completion:^(BOOL finished) {

        [UIView setAnimationBeginsFromCurrentState:YES];
        [UIView transitionWithView:view duration:flipSpeed options:UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionTransitionFlipFromRight animations:
         ^{
             snapshotView.frame = view.bounds;
             [view addSubview:snapshotView];

         } completion:^(BOOL finished) {
             [originalSuperView addSubview:presentedView];
             [snapshotView removeFromSuperview];
         }
         ];
    }];
}
2
Hai Feng Kao

Je fais quelque chose d'assez similaire dans l'un de mes projets ayant une grille d'arts d'album. C'est l'approche que je suis en train de prendre. La clé est d'utiliser CAAnimationGroup.

1) L’ensemble de l’animation comprendrait la mise à l’échelle, la rotation et le déplacement le long d’un même chemin, en premier lieu pour la couche d’album, puis pour la couche de vue modale.

2) Animez la couche d’album en la retournant à 90 degrés, en la redimensionnant légèrement et en vous déplaçant vers un emplacement prédestiné à partir de son emplacement actuel. À ce stade, il disparaîtra (verticalement à l'écran).

3) Ajoutez la vue modale. Mettez à l'échelle et transformez la vue modale pour qu'elle se trouve à l'emplacement exact où la pochette de l'album est positionnée à l'étape 1.

4) Animez la vue modale à partir de cette position en mettant à l’échelle, en tournant et en vous déplaçant le long d’un chemin pour remplir l’écran.

5) Supprimez la vue modale.

6) Présenter la vue modale sans animation.

7) Le chemin choisi devrait normalement ajouter le centre de l'écran en tant que point de contrôle. Mais cela peut être modifié en fonction de la manière dont vous souhaitez que l'animation apparaisse.

Ci-dessous se trouve une fonction où vous pouvez voir l’utilisation du groupe d’animation. J'espère que cela vous aide. Je n'ai toujours pas compris comment éviter l'écrêtage de l'animation par les barres de navigation et les barres d'onglets. :)

+ (void)animateWithCurrentView:(UIView *)currentView 
{
    #define kResizeKey @"bounds.size"
    #define kPathMovement @"position"
    #define kRotation @"transform"
    #define kGroupAnimation @"subviewBeingAnimated"
    #define kLayerAnimation @"animateLayer"

    //flip the view by 180 degrees in its place first.
    currentView.layer.transform = CATransform3DRotate(currentView.layer.transform,radians(180), 0, 1, 0);

    //set the anchor point so that the view rotates on one of its sides.
    currentView.layer.anchorPoint = CGPointMake(0.0, 0.5);



    /**
     * Set up scaling
     */
    CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:kResizeKey];

    //we are going to fill the screen here. So 320,480
    [resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(320, 480)]];
    resizeAnimation.fillMode            = kCAFillModeForwards;
    resizeAnimation.removedOnCompletion = NO;


    /**
     * Set up path movement
     */
    UIBezierPath *movePath = [UIBezierPath bezierPath];

    //the control point is now set to centre of the filled screen. Change this to make the path different.
    CGPoint ctlPoint       = CGPointMake(160.0, 240.0);

    //This is the starting point of the animation. This should ideally be a function of the frame of the view to be animated. Hardcoded here.
    [movePath moveToPoint:CGPointMake(320, 60)];

    //The anchor point is going to end up here at the end of the animation.
    [movePath addQuadCurveToPoint:CGPointMake(0, 240) controlPoint:ctlPoint];

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:kPathMovement];

    moveAnim.path                = movePath.CGPath;
    moveAnim.removedOnCompletion = YES;

    /**
     * Setup rotation animation
     */
    CABasicAnimation* rotateAnimation = [CABasicAnimation animationWithKeyPath:kRotation];
    //start from 180 degrees (done in 1st line)
    CATransform3D fromTransform       = CATransform3DMakeRotation(radians(180), 0, 1, 0);
    //come back to 0 degrees
    CATransform3D toTransform         = CATransform3DMakeRotation(radians(0), 0, 1, 0);

    //This is done to get some perspective.
    CATransform3D persp1 = CATransform3DIdentity;
    persp1.m34 = 1.0 / -3000;

    fromTransform = CATransform3DConcat(fromTransform, persp1);
    toTransform = CATransform3DConcat(toTransform,persp1);

    rotateAnimation.toValue             = [NSValue valueWithCATransform3D:toTransform];
    rotateAnimation.fromValue           = [NSValue valueWithCATransform3D:fromTransform];
    //rotateAnimation.duration            = 2;
    rotateAnimation.fillMode            = kCAFillModeForwards;
    rotateAnimation.removedOnCompletion = NO;

    /**
     * Setup and add all animations to the group
     */
    CAAnimationGroup *group = [CAAnimationGroup animation]; 

    [group setAnimations:[NSArray arrayWithObjects:moveAnim,rotateAnimation, resizeAnimation, nil]];

    group.fillMode            = kCAFillModeForwards;
    group.removedOnCompletion = NO;
    group.duration            = 0.7f;
    group.delegate            = self;

    [group setValue:currentView forKey:kGroupAnimation];

    /**
     * ...and go
     */
    [currentView.layer addAnimation:group forKey:kLayerAnimation];

}
1
Anil Puttabuddhi

Vous pouvez utiliser ceci..Pour application basée sur un contrôleur de navigation ..

YourViewController *obj = [[YourViewController alloc]initWithNibName:@"YourViewControllerXIBName" bundle:nil];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:YES];
[self.navigationController pushViewController:obj animated:NO];
[UIView commitAnimations];
[obj release];
1
Mehul Mistri

Si vous pouvez d'abord obtenir la vue modale pour montrer où vous le voulez sans animation. Devrait être droit devant, et définir les limites/le cadre de view.superview peut être utile sur le style de feuille.

Si cela est résolu, vous pouvez alors exécuter votre animation "vue pure" pour y accéder, puis supprimez la vue et "présentez" le contrôleur modal (qui contrôle cette vue) pour permuter instantanément le contrôleur en contrôle logique de la vue. hiérarchie.

0
bshirley