效果圖:

1.封裝好的工具類(可以根據(jù)需求改變圓心的位置再封裝)
WeCircleAnimation.h文件
#import<Foundation/Foundation.h>
#import<UIKit/UIKit.h>
@interface WeCircleAnimation :NSObject<UIViewControllerTransitioningDelegate>
@end
WeCircleAnimation.m文件
@interface WeCircleAnimation ()<UIViewControllerAnimatedTransitioning,CAAnimationDelegate>
//動畫是呈現(xiàn)還是解除
@property (nonatomic, assign) BOOL isPresented;
//保存動畫上下文(必須要弱引用,如果強引用則會導致循環(huán)引用,內(nèi)存泄露)
@property (nonatomic, weak) id<UIViewControllerContextTransitioning> transitionContext;
@end
@implementation WeCircleAnimation
//告訴控制器誰來提供展現(xiàn)轉(zhuǎn)場動畫
- (nullable id)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{? ??
self.isPresented = YES;? ? return self;
}
//告訴控制器誰來提供解除轉(zhuǎn)場動畫
- (nullable id)animationControllerForDismissedController:(UIViewController *)dismissed{
self.isPresented = NO;
return self;
}
/** 返回動畫時長 @param transitionContext 轉(zhuǎn)場上下文 @return 轉(zhuǎn)場動畫 */
- (NSTimeInterval)transitionDuration:(nullable id)transitionContext{
return 0.3;
}
** 轉(zhuǎn)場動畫最核心的方法-有程序員提供自己的動畫實現(xiàn) @param transitionContext 轉(zhuǎn)場上下文提供轉(zhuǎn)場動畫的所有細節(jié) 容器視圖-轉(zhuǎn)場動畫的表演的舞臺 轉(zhuǎn)場上下文會對展現(xiàn)的控制器強引用 */
- (void)animateTransition:(id)transitionContext{
//1.獲取容器視圖
UIView *containerView = transitionContext.containerView;
//2.獲取目標視圖,如果是展現(xiàn)去toView,如果是解除,去fromView;
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
UIView *view = self.isPresented?toView:fromView;
NSLog(@"%@---%@",fromView,toView);
//3.添加目標視圖到容器視圖
[containerView addSubview:view];
//4.動畫
[self layerAnimationWithView:view];
//5.一定要完成轉(zhuǎn)場,如果不完成,系統(tǒng)會一直等待轉(zhuǎn)場完成,就無法接收用戶的任何交互
//應該在動畫完成之后在通知系統(tǒng)轉(zhuǎn)場結(jié)束
self.transitionContext = transitionContext;
}
-(void)layerAnimationWithView:(UIView *)view{
//1.創(chuàng)建形狀圖層ShapeLayer
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
//圓的屏幕邊距
CGFloat mergin = 20;
//圓的半徑
CGFloat radiu = 25;
CGRect startRect = CGRectMake(view.bounds.size.width - mergin - radiu*2, mergin, radiu*2, radiu*2);
//2.創(chuàng)建圓形貝塞爾路徑 參數(shù)是圓的外界矩形
UIBezierPath *startBezierPath = [UIBezierPath bezierPathWithOvalInRect:startRect];
CGFloat sWidth = view.bounds.size.width;
CGFloat sHeight = view.bounds.size.height;
//結(jié)束圓的半徑(屏幕的半徑)
CGFloat endRadius = sqrt(sWidth * sWidth + sHeight * sHeight);
//使用縮進矩形創(chuàng)建圓的外接矩形
CGRect endRect = CGRectInset(startRect, -endRadius, -endRadius);
//3.外接圓的的貝塞爾路徑
UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:endRect];
//設(shè)置形狀圖層的填充顏色 fillColor:填充圓 strokeColor:邊框圓
//? ? shapeLayer.fillColor = [UIColor redColor].CGColor;
//4.繪制號的貝塞爾路徑為形狀圖層的路徑
shapeLayer.path = startBezierPath.CGPath;
//設(shè)置shapeLayer為控制器視圖的遮罩圖層
//5.設(shè)置mask遮罩圖層:1.會裁切圖層,讓圖層只能看見shapelayer的形狀的區(qū)域 2.一旦將layer設(shè)置為遮罩圖層之后,填充顏色會失效
view.layer.mask = shapeLayer;
//將shape添加到視圖中,addSublayer實在當前的圖層上添加一個layer的形狀區(qū)域
//? ? [self.view.layer addSublayer:shapeLayer];
//5.動畫 -如果要做layer視圖的動畫,不能使用UIView的動畫,應該使用核心動畫
//? ? [UIView animateWithDuration:3 animations:^{
//? ? ? ? ? ? ? ? //設(shè)置圖層的路徑
//? ? ? ? layer.path = endPath.CGPath;
//
//? ? }];
//6.實例化對象
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
//7.設(shè)置動畫的屬性:1.時間? 2.填充模式
//設(shè)置動畫屬性
//時長
animation.duration = [self transitionDuration:self.transitionContext];
//判斷是展現(xiàn)還是解除
if (self.isPresented) {
//formValue
animation.fromValue = (__bridge id _Nullable)(startBezierPath.CGPath);
animation.toValue = (__bridge id _Nullable)(endPath.CGPath);
}else{
animation.fromValue = (__bridge id _Nullable)(endPath.CGPath);
animation.toValue = (__bridge id _Nullable)(startBezierPath.CGPath);
}
//設(shè)置向前填充模式
animation.fillMode = kCAFillModeForwards;
//完成之后不刪除
animation.removedOnCompletion = NO;
//設(shè)置動畫代理 必須要寫在添加動畫到圖層的前面,否則代理不會調(diào)用(一旦將動畫添加到圖層,動畫已經(jīng)開啟,在設(shè)置代理已經(jīng)來不及了)
animation.delegate = self;
//8.將動畫添加到圖層 -shapeLayer,讓哪個圖層執(zhí)行動畫就應該將動畫添加到哪個圖層
[shapeLayer addAnimation:animation forKey:nil];
}
/**
監(jiān)聽動畫完成
@param anim 動畫
@param flag 完成
*/
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
[self.transitionContext completeTransition:YES];
}
@end
運用:
ViewController.m?
#import "ViewController.h"
#import "WeCircleAnimation.h"
@interface ViewController ()
@property (nonatomic, strong) WeCircleAnimation *circleAnnimation;
@end
@implementation ViewController
-(void)awakeFromNib{?
?? [super awakeFromNib];? ??
//1.設(shè)置展示樣式為自定義? ??
self.modalPresentationStyle = UIModalPresentationCustom;??
? //2.設(shè)置轉(zhuǎn)場代理? ??
self.circleAnnimation = [[WeCircleAnimation alloc]init];? ? self.transitioningDelegate = self.circleAnnimation;
}
- (void)viewDidLoad {? ? [super viewDidLoad];}-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event{
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
storyBoard中的控件效果圖
