push動畫spring
#import <UIKit/UIKit.h> @interface ViewController : UIViewController <UINavigationControllerDelegate> @property (weak, nonatomic) IBOutlet UIButton *pushBtn; @end
#import "ViewController.h" #import "Transition.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)viewWillAppear:(BOOL)animated { self.navigationController.delegate = self; } // 導航欄代理方法 - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { if (operation == UINavigationControllerOperationPush) { return [Transition new]; }else{ return nil; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
push動畫對象動畫
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import "POP.h" @interface Transition : NSObject <UIViewControllerAnimatedTransitioning> @end
#import "Transition.h" #import "ViewController.h" #import "TransferViewController.h" @interface Transition ()<CAAnimationDelegate> @property (nonatomic,strong)id<UIViewControllerContextTransitioning> transitionContext; @end @implementation Transition - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext{ return 0.7f; } - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { self.transitionContext = transitionContext; ViewController * fromVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; TransferViewController * toVC = (TransferViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIView * contView = [transitionContext containerView]; UIButton * button = fromVC.pushBtn; UIBezierPath * maskStartBP = [UIBezierPath bezierPathWithOvalInRect:button.frame]; [contView addSubview:fromVC.view]; [contView addSubview:toVC.view]; //建立兩個圓形的 UIBezierPath 實例;一個是 button 的 size ,另一個則擁有足夠覆蓋屏幕的半徑。最終的動畫則是在這兩個貝塞爾路徑之間進行的 CGPoint finalPoint; //判斷觸發點在那個象限 if(button.frame.origin.x > (toVC.view.bounds.size.width / 2)){ if (button.frame.origin.y < (toVC.view.bounds.size.height / 2)) { //第一象限 finalPoint = CGPointMake(button.center.x - 0, button.center.y - CGRectGetMaxY(toVC.view.bounds)+30); }else{ //第四象限 finalPoint = CGPointMake(button.center.x - 0, button.center.y - 0); } }else{ if (button.frame.origin.y < (toVC.view.bounds.size.height / 2)) { //第二象限 finalPoint = CGPointMake(button.center.x - CGRectGetMaxX(toVC.view.bounds), button.center.y - CGRectGetMaxY(toVC.view.bounds)+30); }else{ //第三象限 finalPoint = CGPointMake(button.center.x - CGRectGetMaxX(toVC.view.bounds), button.center.y - 0); } } CGFloat radius = sqrt((finalPoint.x * finalPoint.x) + (finalPoint.y * finalPoint.y)); UIBezierPath * maskFinalBP = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)]; //建立一個 CAShapeLayer 來負責展現圓形遮蓋 CAShapeLayer * maskLayer = [CAShapeLayer layer]; maskLayer.path = maskFinalBP.CGPath; //將它的 path 指定爲最終的 path 來避免在動畫完成後會回彈 toVC.view.layer.mask = maskLayer; CABasicAnimation * maskLayerAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; maskLayerAnimation.fromValue = (__bridge id)(maskStartBP.CGPath); maskLayerAnimation.toValue = (__bridge id)((maskFinalBP.CGPath)); maskLayerAnimation.duration = [self transitionDuration:transitionContext]; maskLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; maskLayerAnimation.delegate = self; [maskLayer addAnimation:maskLayerAnimation forKey:@"path"]; /* POP的彈框效果 CGPathRef CAKeyframeAnimation *keyFrame = [CAKeyframeAnimation animationWithKeyPath:@"path"]; keyFrame.values = @[(__bridge id)(maskStartBP.CGPath),(__bridge id)(maskFinalBP.CGPath)]; keyFrame.duration = 100.0f; keyFrame.additive = YES; keyFrame.removedOnCompletion = NO; keyFrame.fillMode = kCAFillModeForwards; [maskLayer addAnimation:keyFrame forKey:nil]; maskLayer.speed = 0.0; POPAnimatableProperty* pop = [POPAnimatableProperty propertyWithName:@"timeOffset" initializer:^(POPMutableAnimatableProperty *prop) { // read value prop.readBlock = ^(CAShapeLayer *obj, CGFloat values[]) { values[0] = obj.timeOffset; }; // write value prop.writeBlock = ^(CAShapeLayer *obj, const CGFloat values[]) { obj.timeOffset = values[0]; }; // dynamics threshold prop.threshold = 0.1; }]; POPSpringAnimation *popSpring = [POPSpringAnimation animation]; popSpring.fromValue = @(0.0); popSpring.toValue = @(100.f); popSpring.springBounciness = 1.0;//彈性 popSpring.springSpeed = 20.0;//速度 popSpring.dynamicsTension = 700;//張力 popSpring.dynamicsFriction = 5; // 摩擦力 popSpring.dynamicsMass = 1; popSpring.property = pop; popSpring.delegate = self; [maskLayer pop_addAnimation:popSpring forKey:nil]; */ // kPOPShapeLayerStrokeStart //建立一個關於 path 的 CABasicAnimation 動畫來從 circleMaskPathInitial.CGPath 到 circleMaskPathFinal.CGPath 。同時指定它的 delegate 來在完成動畫時作一些清除工做 } - (void)pop_animationDidStop:(POPAnimation *)anim finished:(BOOL)finished { //告訴 iOS 這個 transition 完成 [self.transitionContext completeTransition:![self. transitionContext transitionWasCancelled]]; //清除 fromVC 的 mask [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil; [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil; } #pragma mark - CABasicAnimation的Delegate - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { //告訴 iOS 這個 transition 完成 [self.transitionContext completeTransition:![self. transitionContext transitionWasCancelled]]; //清除 fromVC 的 mask [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil; [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil; } @end
pop動畫atom
#import <UIKit/UIKit.h> @interface TransferViewController : UIViewController <UINavigationControllerDelegate> @property (weak, nonatomic) IBOutlet UIButton *popBtn; @end
#import "TransferViewController.h" #import "InvertTransition.h" @interface TransferViewController () @end @implementation TransferViewController { UIPercentDrivenInteractiveTransition * percentTransition; } - (void)viewDidLoad { [super viewDidLoad]; UIScreenEdgePanGestureRecognizer * edgeGes = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(edgePan:)]; edgeGes.edges = UIRectEdgeLeft; [self.view addGestureRecognizer:edgeGes]; } -(void)viewWillAppear:(BOOL)animated{ self.navigationController.delegate = self; } - (IBAction)clickToPop:(id)sender { [self.navigationController popViewControllerAnimated:YES]; } - (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController { return percentTransition; } - (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { if (operation == UINavigationControllerOperationPop) { InvertTransition * invert = [InvertTransition new]; return invert; }else{ return nil; } } - (void)edgePan:(UIPanGestureRecognizer *)recognizer{ CGFloat per = [recognizer translationInView:self.view].x / (self.view.bounds.size.width); per = MIN(1.0,(MAX(0.0, per))); if (recognizer.state == UIGestureRecognizerStateBegan) { percentTransition = [[UIPercentDrivenInteractiveTransition alloc]init]; [self.navigationController popViewControllerAnimated:YES]; }else if (recognizer.state == UIGestureRecognizerStateChanged){ [percentTransition updateInteractiveTransition:per]; }else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled){ if (per > 0.3) { [percentTransition finishInteractiveTransition]; }else{ [percentTransition cancelInteractiveTransition]; } percentTransition = nil; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
pop動畫對象spa
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface InvertTransition : NSObject <UIViewControllerAnimatedTransitioning> @end
#import "InvertTransition.h" #import "ViewController.h" #import "TransferViewController.h" @interface InvertTransition () @property(nonatomic,strong)id<UIViewControllerContextTransitioning>transitionContext; @end @implementation InvertTransition - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext { return 0.7f; } - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { self.transitionContext = transitionContext; TransferViewController * fromVC = (TransferViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; ViewController * toVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIView * contaionerView = [transitionContext containerView]; UIButton * button = toVC.pushBtn; [contaionerView addSubview:toVC.view]; // 這個順序不能變 [contaionerView addSubview:fromVC.view]; UIBezierPath * finalPath = [UIBezierPath bezierPathWithOvalInRect:button.frame]; CGPoint finalPoint; //判斷觸發點在那個象限 if(button.frame.origin.x > (toVC.view.bounds.size.width / 2)){ if (button.frame.origin.y < (toVC.view.bounds.size.height / 2)) { //第一象限 finalPoint = CGPointMake(button.center.x - 0, button.center.y - CGRectGetMaxY(toVC.view.bounds)+30); }else{ //第四象限 finalPoint = CGPointMake(button.center.x - 0, button.center.y - 0); } }else{ if (button.frame.origin.y < (toVC.view.bounds.size.height / 2)) { //第二象限 finalPoint = CGPointMake(button.center.x - CGRectGetMaxX(toVC.view.bounds), button.center.y - CGRectGetMaxY(toVC.view.bounds)+30); }else{ //第三象限 finalPoint = CGPointMake(button.center.x - CGRectGetMaxX(toVC.view.bounds), button.center.y - 0); } } CGFloat radius = sqrt(finalPoint.x * finalPoint.x + finalPoint.y * finalPoint.y); UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)]; CAShapeLayer *maskLayer = [CAShapeLayer layer]; maskLayer.path = finalPath.CGPath; fromVC.view.layer.mask = maskLayer; CABasicAnimation *pingAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; pingAnimation.fromValue = (__bridge id)(startPath.CGPath); pingAnimation.toValue = (__bridge id)(finalPath.CGPath); pingAnimation.duration = [self transitionDuration:transitionContext]; pingAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; pingAnimation.delegate = self; [maskLayer addAnimation:pingAnimation forKey:@"pingInvert"]; } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { [self.transitionContext completeTransition:![self.transitionContext transitionWasCancelled]]; [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil; [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil; } @end