iOS | CAShapeLayer轉場動畫
阿新 • • 發佈:2018-12-11
什麼也不說了,作為一名樂於分享技術的小開發,直接先上個樣式最為直觀貼切,有需要的朋友可以直接拿過去用。
需要demo請點選這裡 :github
在這個demo中,核心為選用畫布CAShapeLayer,因為一般都是用它來處理形狀之類的動畫,結合了貝塞爾曲線來控制路徑,然後使用CABasicAnimation核心動畫來產生所有效果。
首先封裝一個自定義的動畫。
///動畫自定義封裝 -(void)animationWithView:(UIView *)view{ //1.建立layer CAShapeLayer *layer = [[CAShapeLayer alloc]init]; //2.建立貝塞爾路徑(引數為圓的外接矩形) //間距 CGFloat margin = 20; //半徑 CGFloat radius = 25; //螢幕尺寸 CGFloat viewWidth = [UIScreen mainScreen].bounds.size.width; //螢幕高度 CGFloat viewHeight = [UIScreen mainScreen].bounds.size.height; //螢幕對角線 CGFloat endRadius =sqrt(viewHeight*viewHeight +viewWidth*viewWidth); //起始路徑 CGRect startRect = CGRectMake(viewWidth-2*radius-margin, margin, radius*2, radius*2); UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:startRect]; //終止路徑 UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(startRect, -endRadius, -endRadius) ]; //3.設定貝塞爾屬性 //填充顏色 layer.fillColor = [UIColor redColor].CGColor; //4.將貝塞爾作為layer的路徑 layer.path = startPath.CGPath; //將layer作為父檢視的遮罩圖層. view.layer.mask = layer; //5.將path新增到檢視中 //[self.view.layer addSublayer:layer]; //使用核心動畫實現 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"]; //這個屬性是判斷是動畫之前還是動畫之後的。 if (self.isPresent) { animation.fromValue = (__bridge id _Nullable)(startPath.CGPath); animation.toValue = (__bridge id _Nullable)(endPath.CGPath); }else{ animation.fromValue = (__bridge id _Nullable)(endPath.CGPath); animation.toValue = (__bridge id _Nullable)(startPath.CGPath); } animation.delegate = self; //設定動畫屬性 animation.fillMode = kCAFillModeForwards; animation.duration = 2; animation.removedOnCompletion = NO; //新增動畫到圖層 [layer addAnimation:animation forKey:nil]; }
! 這裡要注意這個mask的屬性,設定之後就不需要再額外的add進去,它是一種用於遮罩檢視的效果,並且設定的顏色會失效
在這個動畫中,有三個重要的屬性,號稱“轉場三劍客”。
UIViewControllerAnimatedTransitioning,主要負責轉場的動畫時間和動畫具體內容。
#pragma mark - UIViewControllerAnimatedTransitioning ///轉場動畫時間 - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext{ return 2; } ///轉場動畫的內容 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{ //1.獲取上下文的容器檢視 UIView *containerView = transitionContext.containerView; //2.獲取目標檢視 UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; //3.將目標檢視新增到容器檢視 UIView *aniView = self.isPresent?toView:fromView; [containerView addSubview:aniView]; //4.開始動畫 [self animationWithView:aniView]; self.context = transitionContext; }
CAAnimationDelegate,主要負責監控動畫開始和動畫結束之後。
#pragma mark - CAAnimationDelegate ///動畫開始 - (void)animationDidStart:(CAAnimation *)anim{ } ///每次動畫結束呼叫 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ //5.告訴上下文完成轉場,否則上下文將會一直等待系統通知是否完成. [self.context completeTransition:YES]; }
UIViewControllerTransitioningDelegate,主要負責告訴系統由哪個控制器提供轉場,哪個控制器來解除轉場。
#pragma mark - UIViewControllerTransitioningDelegate
///告訴由誰提供轉場
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
self.isPresent = YES;
return self;
}
///由誰解除轉場
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
self.isPresent = NO;
return self;
}
最後只需要在需要轉場的控制器中使用這個封裝的類即可
-(void)awakeFromNib{
[super awakeFromNib];
//1.設定跳轉樣式
self.modalPresentationStyle = UIModalPresentationCustom;
//2.設定代理
self.animation = [[JanCustomAnimation alloc]init];
self.transitioningDelegate = self.animation;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self dismissViewControllerAnimated:YES completion:nil];
}
需要注意的是設定樣式和代理,必須要優先於viewdidload之前設定,因為這裡涉及到控制器的生命週期的問題。
好了,到這裡就可以實現完整的自定義轉場的酷炫效果了。