Реализация пользовательской анимации для представления модального вида из указанного вида на iPad - PullRequest
25 голосов
/ 07 июля 2011

На iPad у нас гораздо больше возможностей для работы, поэтому представление модальных полноэкранных представлений не является идеальным.

Я знаю, как представлять модальные представления в новом бланке, и близкий подход можно найти наэтот вопрос: iPad iTunes Animation

Проблема в том, что вы не можете выбрать, откуда будет исходить анимация, поэтому она просто по умолчанию и отображается в центре, я хочу настроить ее так, чтобы онапоявляется из определенного места.

Лучший пример, который я могу найти для этой анимации, можно увидеть в первые несколько секунд этого видео

Если кто-то может указать мне правильное направление, используя код,учебные пособия или документация. Я был бы очень признателен!

Обновление:

После некоторых исследований я обнаружил, что это можно сделать с использованием слоев и Core Animation для первой части;а затем анимировать его в модальном представлении formSheet, но я все еще не совсем понимаю, как этого добиться, надеюсь, вы, ребята, можете помочь!

Ответы [ 7 ]

21 голосов
/ 13 июля 2011

То, что я делал, создавало новую категорию для UIViewController следующим образом

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

Это довольно просто. Пожалуйста, дайте мне знать, если это вам поможет.

UPDATE

Я обновил ответ, чтобы использовать анимационные блоки вместо пары [UIView beginAnimations:nil context:nil]; / [UIView commitAnimations].

5 голосов
/ 15 июля 2011

Похоже, что вы по существу после перевода (перемещения) a CALayer при уменьшении и вращении вокруг оси y одновременно. Попробуйте это:

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 должно быть CGPoint в координатном пространстве myLayer.
  • kMyScalingFactor должно быть <1,0 для уменьшения и> 1,0 для увеличения.
  • myRotationAngle должно быть в радианах. Используйте положительные значения для вращения по часовой стрелке и отрицательные значения для против часовой стрелки.

Вам также нужно реализовать обработчик завершения анимации, чтобы анимация «прилипала»:

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

Надеюсь, это поможет.

2 голосов
/ 09 августа 2012

Ответ Михая не обрабатывает перелистывание, как это делает iPad itunes.Я немного изменил это, чтобы перевернуть вид.Вам не нужны всевозможные сумасшедшие вещи CAAnimation, только несколько встроенных функций анимации UIView.

#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 голосов
/ 11 июля 2011

Я получил это раньше, просто анимируя взгляды.

1) Обложка альбома находится в сетке.
2) Переходите к просмотру обложки альбома с помощью анимации сальто.
3) Анимируйте вид, двигаясь по экрану.

Я быстро бросил это вместе.Предполагая, что у вас есть пустой контроллер представления и 3 представления.

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

     }];

}
1 голос
/ 16 августа 2011

Я делаю нечто очень похожее в одном из моих проектов с сеткой обложек альбомов. Это подход, который я использую. Ключ должен использовать CAAnimationGroup.

1) Вся анимация будет включать в себя масштабирование, вращение и перемещение по пути одновременно - сначала для слоя обложки альбома, а затем для слоя модального вида.

2) Анимируйте слой обложки альбома, перевернув его на 90 градусов, немного масштабировав и перейдя в предопределенное место из его текущего местоположения. В этот момент он исчезнет (вертикально к экрану).

3) Добавьте модальное представление. Масштабируйте и трансформируйте модальное представление так, чтобы оно было точно в том месте, в котором обложка альбома расположена на шаге 1.

4) Анимация модального вида из этой позиции путем масштабирования, поворота и перемещения по траектории для заполнения экрана.

5) Удалить модальное представление.

6) Представлен модальный вид без анимации.

7) Выбранный путь обычно добавляет центр экрана в качестве контрольной точки. Но тогда это можно изменить в зависимости от того, как вы хотите, чтобы анимация отображалась.

Ниже приведена функция, где вы можете увидеть использование анимационной группы. Надеюсь, это поможет вам. Я до сих пор не понял, как избежать отсечения анимации с помощью панелей навигации и панелей вкладок. :)

+ (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 голос
/ 07 июля 2011

Вы можете использовать это .. Для приложения на основе контроллера навигации.

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];
0 голосов
/ 12 июля 2011

Если вы можете сначала получить модальный вид, чтобы показать, где вы хотите без анимации.Должен быть прямым, и установка границ / фрейма view.superview может быть полезна для стиля листа.

Если вы решите это, то можете выполнить анимацию «чистого вида», чтобы получить ее, затемудалите представление и «представьте» модальный контроллер (который контролирует это представление), чтобы мгновенно переключить контроллер в логический контроль иерархии представления.

...