1. 程式人生 > >UIButton自定義路徑動畫

UIButton自定義路徑動畫

  • 歡迎同樣喜歡動效的工程師/UI設計師/產品加入我們
  • iOS動效特攻隊–>QQ群:547897182
  • iOS動效特攻隊–>熊熊:648070256

之前看了一個別人做的漢堡動畫的動效,非常有意思,然後在花瓣網上找了一個差不多的,自己嘗試著做了一下。

花瓣網上找的動效
這裡寫圖片描述

說一下,這個動效主要用的就是路徑動效,都是在CGPath上操作的。蝦面開始講解制作過程。

準備工作

開動!

1,用Sketch繪製圖形,一個圓圈和裡面的勾,注意,勾和圓圈的路徑要連起來的。具體的Sketch使用教程就不說了,自己問度娘。我也是自己慢慢摸索出來的。
實在不會的我這裡有一個我做好的sketch檔案,下載下來直接開啟就行,可以參考一下

http://download.csdn.net/detail/xiongbaoxr/9461562
這裡寫圖片描述

2,將Sketch繪製的圖形以SVG格式匯出,Sketch介面右下角就能看到
這裡寫圖片描述

3,將SVG檔案拖到PaintCode,就會自動生成路徑程式碼
這裡寫圖片描述

4,現在開始程式碼部分
先新建一個UIButton類,先貼出程式碼,程式碼不是很多,蝦面開始一一解釋

#import "CheckBtn.h"
#import "CALayer+CheckBtnLayer.h"

static CGFloat checkStrokeStart     = 0.038;
static CGFloat checkStrokeEnd       = 0.195
; static CGFloat circleStrokeStart = 0.28; static CGFloat circleStrokeEnd = 1; @interface CheckBtn () { CAShapeLayer *checkShapeLayer; } @end @implementation CheckBtn - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (!self) { self = nil
; } self.layer.borderWidth = 3.0f; self.layer.cornerRadius = self.frame.size.width/2.0; self.layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:0.3].CGColor; checkShapeLayer = [CAShapeLayer layer]; checkShapeLayer.path = [self checkPath]; checkShapeLayer.lineWidth = 3.0f; checkShapeLayer.lineCap = kCALineCapRound; checkShapeLayer.lineJoin = kCALineJoinRound; checkShapeLayer.strokeColor = [UIColor whiteColor].CGColor; checkShapeLayer.fillColor = [UIColor clearColor].CGColor; checkShapeLayer.actions = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNull null],@"strokeStart",[NSNull null],@"strokeEnd", nil]; [self.layer addSublayer:checkShapeLayer]; self.showCheck = NO; return self; } @synthesize showCheck = _showCheck; - (void)setShowCheck:(BOOL)showCheck { _showCheck = showCheck; CABasicAnimation *strokeStart = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; CABasicAnimation *strokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; if (self.showCheck) { strokeStart.toValue = [NSNumber numberWithFloat:checkStrokeStart]; strokeStart.duration = 0.4; strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :0.4 :0.3 :1.6]; strokeEnd.toValue = [NSNumber numberWithFloat:checkStrokeEnd]; strokeEnd.duration = 0.5; strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.15 :0.6 :0.6 :1]; }else{ strokeStart.toValue = [NSNumber numberWithFloat:circleStrokeStart]; strokeStart.duration = 0.4; strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.4 :0.58 :0.88]; strokeEnd.toValue = [NSNumber numberWithFloat:circleStrokeEnd]; strokeEnd.duration = 0.55; strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.08 :0.64 :0.1]; } [checkShapeLayer ocb_applyAnimation:strokeStart]; [checkShapeLayer ocb_applyAnimation:strokeEnd]; } - (CGPathRef)checkPath { UIBezierPath* oval1DrawPath = UIBezierPath.bezierPath; [oval1DrawPath moveToPoint: CGPointMake(33.85, 11.21)]; [oval1DrawPath addLineToPoint: CGPointMake(16.5, 26.07)]; [oval1DrawPath addLineToPoint: CGPointMake(2.95, 10.33)]; [oval1DrawPath addCurveToPoint: CGPointMake(3.57, 9.3) controlPoint1: CGPointMake(2.95, 10.33) controlPoint2: CGPointMake(3.21, 9.86)]; [oval1DrawPath addCurveToPoint: CGPointMake(4.33, 8.2) controlPoint1: CGPointMake(3.77, 8.98) controlPoint2: CGPointMake(4.04, 8.59)]; [oval1DrawPath addCurveToPoint: CGPointMake(6.12, 6.11) controlPoint1: CGPointMake(4.87, 7.45) controlPoint2: CGPointMake(5.59, 6.62)]; [oval1DrawPath addCurveToPoint: CGPointMake(8.53, 4.08) controlPoint1: CGPointMake(6.69, 5.54) controlPoint2: CGPointMake(7.62, 4.72)]; [oval1DrawPath addCurveToPoint: CGPointMake(10.6, 2.79) controlPoint1: CGPointMake(9.59, 3.33) controlPoint2: CGPointMake(10.6, 2.79)]; [oval1DrawPath addCurveToPoint: CGPointMake(28.09, 2.65) controlPoint1: CGPointMake(10.6, 2.79) controlPoint2: CGPointMake(18.78, -2.09)]; [oval1DrawPath addCurveToPoint: CGPointMake(36.36, 28.09) controlPoint1: CGPointMake(37.4, 7.39) controlPoint2: CGPointMake(41.1, 18.78)]; [oval1DrawPath addCurveToPoint: CGPointMake(10.91, 36.36) controlPoint1: CGPointMake(31.61, 37.4) controlPoint2: CGPointMake(20.22, 41.1)]; [oval1DrawPath addCurveToPoint: CGPointMake(0.71, 21.82) controlPoint1: CGPointMake(5.18, 33.44) controlPoint2: CGPointMake(1.47, 27.88)]; [oval1DrawPath addCurveToPoint: CGPointMake(0.59, 18.86) controlPoint1: CGPointMake(0.59, 20.84) controlPoint2: CGPointMake(0.55, 19.85)]; [oval1DrawPath addCurveToPoint: CGPointMake(1.06, 15.25) controlPoint1: CGPointMake(0.64, 17.66) controlPoint2: CGPointMake(0.78, 16.45)]; [oval1DrawPath addCurveToPoint: CGPointMake(1.79, 12.78) controlPoint1: CGPointMake(1.09, 15.12) controlPoint2: CGPointMake(1.36, 13.92)]; [oval1DrawPath addCurveToPoint: CGPointMake(2.91, 10.39) controlPoint1: CGPointMake(2.22, 11.67) controlPoint2: CGPointMake(2.9, 10.41)]; CGPathRef pathRef = oval1DrawPath.CGPath; return pathRef; }

4.1 建立半透明白色圓環作為基底

self.layer.borderWidth = 3.0f;
self.layer.cornerRadius = self.frame.size.width/2.0;
self.layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:0.3].CGColor;

4.2 新建一個CAShaperLayer物件CAShapeLayer *checkShapeLayer; 配置checkShapeLayer的引數

checkShapeLayer = [CAShapeLayer layer];
checkShapeLayer.path = [self checkPath]; //這裡的checkPath就是之前我們生成的path路徑
checkShapeLayer.lineWidth = 3.0f;//線寬
checkShapeLayer.lineCap = kCALineCapRound;//layer邊緣的樣式
checkShapeLayer.lineJoin = kCALineJoinRound;//layer銜接部分的樣式
checkShapeLayer.strokeColor = [UIColor whiteColor].CGColor;//layer描邊的顏色,
checkShapeLayer.fillColor = [UIColor clearColor].CGColor;//layer填充的顏色,注意,這和stroke不一樣
checkShapeLayer.actions = [[NSDictionary alloc] initWithObjectsAndKeys:
                     [NSNull null],@"strokeStart",[NSNull null],@"strokeEnd", nil];//layer增加動畫事件

[self.layer addSublayer:checkShapeLayer];

4.3 後面設定按鈕點選動畫

4.3.1 這四個引數分別是“Check”和“Circle”這兩種不同形狀時的strokeStart和strokeEnd的位置,有效值為0~1

static CGFloat checkStrokeStart     = 0.038;
static CGFloat checkStrokeEnd       = 0.195;
static CGFloat circleStrokeStart    = 0.28;
static CGFloat circleStrokeEnd      = 1;

4.3.2 建立CABasicAnimation strokeStart和strokeEnd,為什麼 @”strokeStart”和@”strokeEnd”能建立動畫,官方文件上說了,這兩個屬性是Animation的,可以建立動畫的,建議大家看看CAShapeLayer的官方文件,不長,而且容易看懂。

strokeStart.toValue 動畫的最終位置
strokeStart.duration 動畫之行的時長
strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :0.4 :0.3 :1.6];動畫的步速
這是一種通過貝塞爾曲線的方式來調整速度的方式,比如先快後慢,或者先慢後快。看下面的文章就容易理解了。
參考文章
http://netcetera.org/camtf-playground.html
https://isux.tencent.com/ios-easing-tween-animation.html

@synthesize showCheck = _showCheck;
- (void)setShowCheck:(BOOL)showCheck
{
    _showCheck = showCheck;

    CABasicAnimation *strokeStart = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
    CABasicAnimation *strokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];

    if (self.showCheck) {

        strokeStart.toValue = [NSNumber numberWithFloat:checkStrokeStart];
        strokeStart.duration = 0.4;
        strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :0.4 :0.3 :1.6];

        strokeEnd.toValue = [NSNumber numberWithFloat:checkStrokeEnd];
        strokeEnd.duration = 0.5;
        strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.15 :0.6 :0.6 :1];
    }else{

        strokeStart.toValue = [NSNumber numberWithFloat:circleStrokeStart];
        strokeStart.duration = 0.4;
        strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.4 :0.58 :0.88];

        strokeEnd.toValue = [NSNumber numberWithFloat:circleStrokeEnd];
        strokeEnd.duration = 0.55;
        strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.08 :0.64 :0.1];
    }

    [checkShapeLayer ocb_applyAnimation:strokeStart];
    [checkShapeLayer ocb_applyAnimation:strokeEnd];
}

在CALayer的基礎上擴充套件的一個方法,
animation.fromValue = [self.presentationLayer valueForKey:animation.keyPath];從動畫演示層presentationLayer 獲取之前的狀態,並且設定為fromValue
再設定toValue,和animation開始執行動畫

- (void)ocb_applyAnimation:(CABasicAnimation *)animation
{
    if (animation.fromValue == nil) {
        animation.fromValue = [self.presentationLayer valueForKey:animation.keyPath];
    }

    [self addAnimation:animation forKey:animation.keyPath];
    [self setValue:animation.toValue forKey:animation.keyPath];
}

這樣,一個有趣的動效就完成了。做的過程中遇到不懂的問度娘或者看官方文件都行,官方文件對這一塊的解釋都比較清楚,能看懂。歡迎有同樣喜歡動效的程式設計師和設計師加入我。QQ:648070256