昨天和大家分享了一下xib上的一些細節上的使用技巧,今天和大家分享以下本人第一次做出的比較好看的一個動畫,到現在仍覺得這種效果很流行,且好看,是利用layer.mask屬性所制作的,廢話不多說,先效果圖.
又看見咱們可愛的夏娜醬~,這是簡單的遮罩轉場動畫,iOS7開始,蘋果就已經推出了自定義轉場的的API,可以使用CoreAnimation實現轉場動畫,更加可以出現在兩個VC之間任意切換,真正實現了高度解耦,真的是想怎么來就怎么來,只要你能夠有創意就行了.下面為大家介紹一下這個簡單`的轉場動畫是怎么做的.
1.首先在storyBoard上的布局是這樣的
放大后可以看到push按鈕和pop按鈕,使navigationller.navigationBar透明
2.接着為紅色夏娜的`VC綁定ViewController,第二個黑發夏娜VC綁定一個SecondViewController;
3.為兩個VC簽訂協議<
UINavigationControllerDelegate
>
實例如下:
@interface ViewController : UIViewController<UINavigationControllerDelegate>
@property (weak, nonatomic) IBOutlet UIButton *button;
@end
@interface SecondViewController : UIViewController<UINavigationControllerDelegate>
@property (weak, nonatomic) IBOutlet UIButton *button;
@end
我們來先講push效果是如何做的
4.重寫ViewController的ViewWillAppear的方法,制定navigationController的代理人是自己
- (void)viewWillAppear:(BOOL)animated
{
self.navigationController.delegate = self;
}
5.實現代理方法
#pragma mark --UINavigationControllerDelegate
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
if (operation == UINavigationControllerOperationPush) {
OvalTransition *ovalTransi = [OvalTransition new];
return ovalTransi;
}else{
return nil;
}
}
這個代理方法需要返回一個簽了UIViewControllerAnimatedTransitioning協議的對象,而我返回的是自定義的OvralTransition對象
我們來看這一個對象的.h文件下的代碼
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface OvalTransition : NSObject<UIViewControllerAnimatedTransitioning>
@end
6.現在為這一個類簽訂UIViewControllerAnimatedTransitioning協議.
7.該類的實現如下
// Created by 葉楊 on 16/3/15.
// Copyright © 2016年 葉景天. All rights reserved.
//
#import "OvalTransition.h"
#import "ViewController.h"
#import "SecondViewController.h"
@interface OvalTransition ()
//在類的延展里頭添加一個簽了轉場動畫代理方法的上下文對象,方便之后的傳值
@property (nonatomic, strong)id<UIViewControllerContextTransitioning>transitionContext;
@end
@implementation OvalTransition
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.7;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
//賦值給屬性,動畫結束時方便傳值
self.transitionContext = transitionContext;
//獲取前一個VC
ViewController *fromVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//獲取后一個VC
SecondViewController *toVC = (SecondViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//獲取轉場時生成的轉場視圖view
UIView *containerView = [transitionContext containerView];
UIButton *button = fromVC.button;
UIBezierPath *maskStartBP = [UIBezierPath bezierPathWithOvalInRect:button.frame];
[containerView addSubview:fromVC.view];
[containerView addSubview:toVC.view];
//創建兩個圓形的UIBezierPath實例: 一個是button的size,另外一個則擁有足夠覆蓋屏幕的半徑.最終的動畫是在這兩個圓周路徑之中進行的
//這里的finalPoint是相對於fromVC.button.center的位置,並不是在屏幕上的位置
CGPoint finalPoint;
//判斷觸發在哪一個象限,當然我們這里很明顯是在第一象限,但是這里為了代碼的可復用性,我們也必須判斷再計算
if (button.frame.origin.x > (toVC.view.bounds.size.width / 2)) {
if (button.frame.origin.y < (toVC.view.bounds.size.width / 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);
}
}
//CGRectInset是返回一個中心點一樣但是寬高不一樣的矩形
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 來避免在動畫完成后會回彈
//這里說明一下mask的屬性,mask的屬性很簡單,例如:view上加了一層imageView,如果imageView.layer.mask = layerA,那么layerA上不透明的部分將會被繪制成透明,透明的部分將會把imageView.layer繪制成白色
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"];
}
#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
//最終push完成的效果就是這樣的
這樣就完成了一個簡單的遮罩push效果,至於pop的話大家可以根據前面的例子自己做,具體Demo在可以在我的gitHub上下載,下載地址https://github.com/bnb173yjx/maskAnimationDemo 因為storyBoard上沒有做適配,所以打開的時候以6plus模擬器打開