扩展视图控制器之间的圆形动画过渡

时间:2014-07-11 19:48:46

标签: ios uiviewanimation uiviewanimationtransition

我使用过渡委托创建了自定义动画过渡:

vc.modalTransitionStyle = UIModalPresentationCustom;
vc.transitioningDelegate = self.transitioningDelegate;

转换代表工作正常,但我正在努力实现迄今为止我没有成功完成的效果。 我试图使新的视图控制器看起来像这样的效果:

Cool animated tranisitoning

我尝试过做CGAffineTransformMakeScale,但它放大了新的视图控制器视图,而不仅仅是“将视图圈扩展到它”。当尝试使用动画更改边界和边框时,它总是最适合圆圈与屏幕边界,并且不像我给出的动画那样完全打开圆圈。

有什么想法吗?

感谢。

2 个答案:

答案 0 :(得分:14)

我对这个问题的第一个答案是我之前制作的应用程序的修改版本,现在不是最好的方法(iOS 7及更高版本)。在iOS 7中,我们可以使用UIViewControllerTransitioningDelegate和相关的委托和类创建自定义转换。此版本封装了动画对象(符合UIViewControllerAnimatedTransitioning的对象)中的所有蒙版创建和大小调整,因此它使控制器代码更简单,允许您使用任何控制器作为目标控制器而无需特殊代码。以下是我在课堂上使用的代码,

#import "ViewController.h"
#import "SecondViewController.h"
#import "RDPresentationAnimator.h"

@interface ViewController () <UIViewControllerTransitioningDelegate>
@property (weak,nonatomic) IBOutlet UIButton *button;
@end

@implementation ViewController

-(IBAction)presentSecondController:(UIButton *)sender {
    self.button = sender;
    SecondViewController *second = [self.storyboard instantiateViewControllerWithIdentifier:@"Second"];
    second.modalTransitionStyle = UIModalPresentationCustom;
    second.transitioningDelegate = self;
    [self presentViewController:second animated:YES completion:nil];
}

-(id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
    RDPresentationAnimator *animator = [RDPresentationAnimator new];
    animator.isPresenting = YES;
    animator.senderFrame = self.button.frame;
    return animator;
}


-(id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    NSLog(@"dismissed delegate called");
    RDPresentationAnimator *animator = [RDPresentationAnimator new];
    animator.isPresenting = NO;
    animator.senderFrame = self.button.frame;
    return animator;
}

这里是动画对象中的代码。 .h,

@interface RDPresentationAnimator : NSObject <UIViewControllerAnimatedTransitioning>

@property (nonatomic) BOOL isPresenting;
@property (nonatomic) CGRect senderFrame;

这是.m,

@interface RDPresentationAnimator ()
@property (strong,nonatomic) UIBezierPath *maskPath;
@property (strong,nonatomic) UIViewController *toVC;
@property (strong,nonatomic) UIViewController *fromVC;
//@property (strong,nonatomic) id <UIViewControllerContextTransitioning> transitionContext;
@end

    @implementation RDPresentationAnimator

    #define ANIMATION_TIME 0.6



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


    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
       // self.transitionContext = transitionContext;
        self.fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        self.toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

        if (self.isPresenting) {
            [transitionContext.containerView addSubview:self.fromVC.view];
            [transitionContext.containerView addSubview:self.toVC.view];

            self.maskPath = [UIBezierPath bezierPathWithOvalInRect:self.senderFrame];
            CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
            maskLayer.frame = self.toVC.view.frame;
            maskLayer.path = self.maskPath.CGPath;
            self.toVC.view.layer.mask = maskLayer;

            UIBezierPath *newPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-self.toVC.view.frame.size.width/2, -self.toVC.view.frame.size.height/2, self.toVC.view.frame.size.height*2, self.toVC.view.frame.size.height*2)];
            CABasicAnimation* pathAnim = [CABasicAnimation animationWithKeyPath: @"path"];
            //pathAnim.delegate = self;
            pathAnim.fromValue = (id)self.maskPath.CGPath;
            pathAnim.toValue = (id)newPath.CGPath;
            pathAnim.duration = ANIMATION_TIME;
            maskLayer.path = newPath.CGPath;
            [maskLayer addAnimation:pathAnim forKey:@"path"];
        }else{
            [transitionContext.containerView addSubview:self.toVC.view];
            [transitionContext.containerView addSubview:self.fromVC.view];
            self.maskPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-self.fromVC.view.frame.size.width/2, -self.fromVC.view.frame.size.height/2, self.fromVC.view.frame.size.height*2, self.fromVC.view.frame.size.height*2)];
            CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
            maskLayer.frame = self.fromVC.view.frame;
            maskLayer.path = self.maskPath.CGPath;
            self.fromVC.view.layer.mask = maskLayer;

            UIBezierPath *newPath = [UIBezierPath bezierPathWithOvalInRect:self.senderFrame];
            CABasicAnimation* pathAnim = [CABasicAnimation animationWithKeyPath: @"path"];
            //pathAnim.delegate = self;
            pathAnim.fromValue = (id)self.maskPath.CGPath;
            pathAnim.toValue = (id)newPath.CGPath;
            pathAnim.duration = ANIMATION_TIME;
            maskLayer.path = newPath.CGPath;
            [maskLayer addAnimation:pathAnim forKey:@"path"];
        }

        [self performSelector:@selector(finishTransition:) withObject:transitionContext afterDelay:ANIMATION_TIME];
    }



    -(void)finishTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
        [transitionContext completeTransition:YES];
    }


    //- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
    //    NSLog(@"In didStop");
    //    if (flag) {
    //        if (self.isPresenting) {
    //            self.toVC.view.layer.mask = nil;
    //            [self.transitionContext completeTransition:YES];
    //        }else{
    //            self.fromVC.view.layer.mask = nil;
    //            [self.transitionContext completeTransition:YES];
    //            self.transitionContext = nil;
    //        }
    //    }
    //}


    -(void)dealloc {
        NSLog(@"In animator dealloc");
    }

这个代码也有反向动画,当你解雇模态控制器时(你只需要调用dismissViewControllerAnimated:completion:就像那个类中的正常一样)。

答案 1 :(得分:3)

我过去曾经做过类似的事情,但它并没有那种扩大的橙色戒指。我的InitialViewController在故事板中添加了一个带有圆形蒙版和背景图像的按钮。我提供的视图控制器有一个掩码,其大小和位置是通过传入触发演示的InitialViewController中按钮的框架来设置的。呈现的视图控制器定义一个协议,其一个方法在动画结束时被调用。这是初始视图控制器中的代码,

#import "InitialViewController.h"
#import "ViewController.h"

@interface InitialViewController () <ExpandingViewDelegate>
@end

@implementation InitialViewController


-(IBAction)expandToDetail:(UIButton *)sender { 
    ViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"VC"];
    vc.delegate = self;
    vc.buttonRect = sender.frame;
    [self.view addSubview:vc.view];
}


-(void)viewFinishedAnimating:(UIViewController *) sender { // delegate method from the presentedViewController
    [sender.view removeFromSuperview];
    [self presentViewController:sender animated:NO completion:nil];
}

这是所呈现的视图控制器中的代码,它执行掩码扩展。 .h文件,

@protocol ExpandingViewDelegate <NSObject>
-(void)viewFinishedAnimating:(UIViewController *) sender;
@end

@interface ViewController : UIViewController

@property (nonatomic) CGRect buttonRect;
@property (weak,nonatomic) id<ExpandingViewDelegate> delegate;
@end

.m文件,

@interface ViewController ()
@property (strong,nonatomic) UIBezierPath *maskPath;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.maskPath = [UIBezierPath bezierPathWithOvalInRect:self.buttonRect];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = self.view.frame;
    maskLayer.path = self.maskPath.CGPath;
    self.view.layer.mask = maskLayer;
    [self enlargeMask:maskLayer];
}

-(void)enlargeMask:(CAShapeLayer *) shapeLayer {

    UIBezierPath *newPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-self.view.frame.size.width/2, -self.view.frame.size.height/2, self.view.frame.size.height*2, self.view.frame.size.height*2)];
    CABasicAnimation* pathAnim = [CABasicAnimation animationWithKeyPath: @"path"];
    pathAnim.delegate = self;
    pathAnim.fromValue = (id)self.maskPath.CGPath;
    pathAnim.toValue = (id)newPath.CGPath;
    pathAnim.duration = 6;
    shapeLayer.path = newPath.CGPath;
    [shapeLayer addAnimation:pathAnim forKey:@"path"];
}

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
    if (flag) {
        self.view.layer.mask = nil;
        [self.delegate viewFinishedAnimating:self];
    }
}