實現一個iOS漸變背景動畫效果的Switch

前言

在dribbble看到一個Switch動畫效果就手癢想實現,下面就是我實現的思路。

源代碼

GitHub地址:VGGradientSwitch
如果覺得不錯,歡迎點star。

設計圖

來自dribbble上的設計作者 Nick Buturishvili

image
image

效果圖

image

思路

  • 首先解刨一下設計圖
  1. 外觀和iOS原生UISwitch相同
  2. 觀察動圖發現Switch背景圖為漸變色,這也是這個開關設計的一大亮點
  3. 開關上的紐扣,打開時狀態是一個勾,關閉時是一個叉。
  4. 打開動畫,勾邊線放大移動邊做形變變成點再變換成叉放大后恢復原狀,背景顏色由青色轉換到橘黃色。
  5. 關閉動畫,叉邊先放大移動邊做形變再變成勾放大后恢復原狀,背景顏色由橘黃色轉換到青色

實現

  • 漸變背景圖是通過CAGradientLayer實現。通過設計圖取色拿到顏色十六進制(0x08ded6,0x18deb9,0xef9c29,0xe76b39)四個顏色,創建出一個switch3倍寬的漸變圖
    如圖:
    gradientBackground.png

代碼如下:

CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.locations = @[@0, @.33, @.63, @1];
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(1, 0);
gradientLayer.frame = CGRectMake(0, 0, self.frame.size.width * 3, self.frame.size.height);
  • 邊框使用UIBezierPath設置一個圓角邊框
border.png
  • 勾、點和叉的實現使用到UIBezierPath提供path,然后CAShapeLayer創建,勾圖形實現代碼如下:
UIBezierPath *tickPath = [UIBezierPath bezierPath];
[tickPath moveToPoint:CGPointMake(self.frame.size.width/8 * 3, self.frame.size.width/2)];
CGPoint p1 = CGPointMake(self.frame.size.width/2, self.frame.size.width/8 * 5);
[tickPath addLineToPoint:p1];
CGPoint p2 = CGPointMake(self.frame.size.width/8 * 6, self.frame.size.width/8 * 3);
[tickPath addLineToPoint:p2];

CAShapeLayer *layer = [[CAShapeLayer alloc] init];
layer.lineCap = kCALineCapRound;
layer.lineJoin = kCALineJoinRound;
layer.fillColor = [UIColor clearColor].CGColor;
layer.strokeColor = [UIColor whiteColor].CGColor;
layer.lineWidth = 2;
layer.path = tickPath.CGPath;
tick.png
  • 漸變背景顏色的動畫效果,筆者是將CAGradientLayer添加到一個UIView上然后直接使用,UIView的動畫方法然后做位移,代碼如下:
[UIView animateKeyframesWithDuration:.5 delay:.1 options:UIViewKeyframeAnimationOptionCalculationModePaced animations:^{
        self.gradientView.frame = CGRectMake(-self.frame.size.width *2, 0, self.frame.size.width *3, self.frame.size.height);
    } completion:^(BOOL finished) {
    
    }];
gradient_animation.gif
  • 圖形形變動畫主要利用Core Animation實現,用到了CAKeyframeAnimationCABasicAnimation、CAAnimationGroup

  • 勾->點動畫 先放大勾后然后做縮小形變成點

  • 點->勾動畫 做形變成勾后做放大

  • 動畫使用了"path"和"transform",形變動畫使用path提供路徑數組,這里提供原本勾的路徑和點的路徑這樣路徑就從勾形變到點
    代碼如下:

  • 放大動畫 使用CABasicAnimation

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
    CATransform3D tr = CATransform3DIdentity;
    tr = CATransform3DTranslate(tr, _rect.size.width/2, _rect.size.height/2, 0);
    tr = CATransform3DScale(tr, 1.2, 1.2, 1);
    tr = CATransform3DTranslate(tr, -_rect.size.width/2, -_rect.size.height/2, 0);
    animation.toValue = [NSValue valueWithCATransform3D:tr];
    animation.autoreverses = YES;
    animation.timingFunction  = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
  • 形變動畫 使用CAKeyframeAnimation具體的使用可以Google一下這里就不多贅言,values傳入的是勾的path和點path
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"path"];
    animation.values = values;
    animation.keyTimes = keyTimes;
    animation.beginTime = beginTime;
  • 使用CAAnimationGroup做組合動畫,組合動畫代理方法可以判斷組合動畫是否完成,<CAAnimationDelegate> 代碼如下:
     // scaleAnimation 放大 lineAnimation線條形變
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = @[scaleAnimation,lineAnimation];
    animationGroup.duration = .5;
    animationGroup.repeatCount = 1;
    animationGroup.removedOnCompletion = NO;
    animationGroup.fillMode = kCAFillModeForwards;
    animationGroup.timingFunction  = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    animationGroup.delegate = self;
    
    // CAAnimationDelegate  動畫是否結束
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    }
1.gif
  • 剩下的就是移動這個按鈕然后由點形變成叉,這里就不再說明,可以直接看GitHub代碼,以上就是分解動畫的一些思路和解決辦法,但是動畫要流暢和交互不違和還需要細微調整
  • 有什么代碼和實現的效果建議可以提issue給我,如果喜歡的話點一下 star 哦
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容