「 iOS 」CAShapeLayer轉場動畫

自定義轉場.gif

在這個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進去,它是一種用於遮罩視圖的效果,而且設置的顏色會失效spa

在這個動畫中,有三個重要的屬性,號稱「轉場三劍客」。代理

UIViewControllerAnimatedTransitioning,主要負責轉場的動畫時間和動畫具體內容。code

#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,主要負責告訴系統由哪一個控制器提供轉場,哪一個控制器來解除轉場。rem

#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;
}

最後只須要在須要轉場的控制器中使用這個封裝的類便可animation

-(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以前設置,由於這裏涉及到控制器的生命週期的問題it

好了,到這裏就能夠實現完整的自定義轉場的酷炫效果了。io

相關文章
相關標籤/搜索