1. 程式人生 > >iOS | CAShapeLayer轉場動畫

iOS | CAShapeLayer轉場動畫

什麼也不說了,作為一名樂於分享技術的小開發,直接先上個樣式最為直觀貼切,有需要的朋友可以直接拿過去用。

自定義轉場.gif

需要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之前設定,因為這裡涉及到控制器的生命週期的問題

好了,到這裡就可以實現完整的自定義轉場的酷炫效果了。