iOS 一個(gè)提交按鈕形變動(dòng)畫設(shè)計(jì)思路

最近看到一個(gè)UI設(shè)計(jì)效果圖,自己嘗試做了一下,代碼最好還是去github上下載下來看吧:

1504118-622ed4419554c027.gif

源碼下載地址:https://github.com/DMDavid/DMSubmitView
最近封裝了一下,使用的話可以

pod 'DMSubmitView'

初步設(shè)計(jì)思路

這個(gè)submit動(dòng)畫可以分開為3部分設(shè)計(jì):

  1. 按鈕形變部分:


    0FF8334F-87CA-4178-8109-B47920A51F70.jpg

這部分按鈕逐漸縮小自身的寬度,當(dāng)自身寬度與高度相等時(shí),則停止形變。

2.圓環(huán)部分:


6E5CE458-AB61-48C8-8E09-7115030C3A40.jpg

這部分沒啥好說的,現(xiàn)在APP上很多l(xiāng)oading的效果,基本都是這樣的。主要就是2個(gè)圖層,下面圖層是灰色的,上面的綠色圖層作為subLayer覆蓋在灰色圖層上面。

3.完成部分:


CC7C6A13-C7C1-4F26-AF5E-0A112454706C.jpg

這是這個(gè)效果的最后部分,即從一個(gè)圓逐漸還原成原來按鈕的大小樣式。然后上面做一個(gè)圖層動(dòng)畫,畫一個(gè)“對(duì)號(hào)”。

代碼實(shí)現(xiàn)

清楚了基本思路后,就可以著手?jǐn)]代碼了。
你可以全部使用圖層來實(shí)現(xiàn),上面效果。但正所謂條條大路通羅馬,這邊提供的方法可做參考,如果有更好的方法設(shè)計(jì),歡迎與我交流。

1.第一部分設(shè)計(jì)實(shí)現(xiàn):

這里我仍然使用button作為點(diǎn)擊的submit按鈕,當(dāng)然你也可以使用其他方法設(shè)計(jì)。接下來創(chuàng)建一個(gè)label來展示文字,為什么不使用button自帶的textLabel?是因?yàn)槿绻褂胋utton自帶的label的話,執(zhí)行動(dòng)畫后自帶label大小也在改變,不是特別方便。當(dāng)然,還是那句話,你也可以封裝一個(gè)button。
之后,我們的層級(jí)關(guān)系應(yīng)該如下圖所示:


370F910E-B93D-4BF3-903C-591013FE1B20.png

這里注意:藍(lán)色為我們的submitView,紅色為btn,淺藍(lán)為label。
而button和label的父視圖都為藍(lán)色的submitView。

將按鈕設(shè)計(jì)為圓角:

 self.submitButton.layer.cornerRadius=self.submitButton.bounds.size.height/2;
 self.submitButton.layer.masksToBounds = YES;

沒什么好說的

在按鈕的點(diǎn)擊事件里面,去做一個(gè)讓按鈕寬度縮小的動(dòng)畫,使用UIView 的隱式動(dòng)畫,隱藏label,另外讓按鈕背景色逐漸變白,并給它添加邊境寬度,顏色設(shè)為灰色。實(shí)現(xiàn)這個(gè)效果:


Paste_Image.png

按鈕點(diǎn)擊事件:

 - (void)submitBtnClick:(UIButton *)submitBtn {
    //縮小動(dòng)畫
    [self scaleLayerAnimtaion];

    [UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        //隱藏按鈕
        self.showLabel.alpha = 0;
        self.submitButton.backgroundColor = [UIColor whiteColor];
        self.submitButton.layer.borderWidth = 2;
        self.submitButton.layer.borderColor = [UIColor colorWithRed:172.0/255.0 green:172.0/255.0 blue:172.0/255.0 alpha:1].CGColor;
    
} completion:^(BOOL finished) {
        self.submitButton.hidden = YES;
        [self drawProgressLayer]; 
}];

}

//縮放動(dòng)畫
- (void)scaleLayerAnimtaion {
    CABasicAnimation *anima = [CABasicAnimation animation];
    anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    anima.duration = 1.;
    anima.keyPath = @"bounds";
    anima.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, _originRect.size.height, _originRect.size.height)];
    anima.removedOnCompletion = NO;
    anima.fillMode = kCAFillModeForwards;
    [self.submitButton.layer addAnimation:anima forKey:nil];
}
注意: 這里UIView隱式動(dòng)畫的duration的時(shí)間應(yīng)該和scaleLayerAnimation的duration相同,否則動(dòng)畫會(huì)出現(xiàn)不圓潤的情況。

如果一切順利,你會(huì)看到下面效果:


part1111.gif
2.第二部分設(shè)計(jì)實(shí)現(xiàn):

這里我們已經(jīng)完成了讓按鈕變成一個(gè)圓環(huán)啦。接下來,其實(shí)你也可以直接使用button的圖層,在它上面創(chuàng)建一個(gè)綠色環(huán)形圖層,執(zhí)行一個(gè)動(dòng)畫,做出loading的效果。當(dāng)然實(shí)現(xiàn)的方法很多,我這里使用的是讓這個(gè)button隱藏,自己重新創(chuàng)建一個(gè)灰色圖層,它的大小樣式都和button形變后的效果一樣,只不過隱式動(dòng)畫比較快,難以區(qū)分,我們?cè)趧?dòng)畫完成block回調(diào)里面創(chuàng)建這個(gè)圖層。

 - (void)submitBtnClick:(UIButton *)submitBtn {
    //縮小動(dòng)畫
    [self scaleLayerAnimtaion];

    [UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        //隱藏按鈕
        self.showLabel.alpha = 0;
        self.submitButton.backgroundColor = [UIColor whiteColor];
        self.submitButton.layer.borderWidth = 2;
        self.submitButton.layer.borderColor = [UIColor colorWithRed:172.0/255.0 green:172.0/255.0 blue:172.0/255.0 alpha:1].CGColor;
    
} completion:^(BOOL finished) {

        //mark - 在這里實(shí)現(xiàn)完成block后的操作!

        self.submitButton.hidden = YES;
        [self drawProgressLayer]; 
}];

}

//進(jìn)度環(huán)動(dòng)畫
-(void) drawProgressLayer {  //方法實(shí)現(xiàn):

    _viewCenter = (CGPoint){self.bounds.size.width/2, self.bounds.size.height/2};
    //1. 背景環(huán)    
    backProgressLayer = [CAShapeLayer layer];
    backProgressLayer.strokeColor = changedBgColor.CGColor;
    backProgressLayer.fillColor = [UIColor whiteColor].CGColor;
    backProgressLayer.lineCap   = kCALineCapRound;
    backProgressLayer.lineJoin  = kCALineJoinBevel;
    backProgressLayer.lineWidth = 2;

    UIBezierPath *backProgressCircle = [UIBezierPath bezierPath];
    [backProgressCircle addArcWithCenter:_viewCenter radius:self.bounds.size.height/2 startAngle:-M_PI_2 endAngle:M_PI_2 * 3 clockwise:YES];
    backProgressLayer.path = backProgressCircle.CGPath;
    [self.layer addSublayer:backProgressLayer];


    //2. 圓環(huán)
    CAShapeLayer *progressLayer = [CAShapeLayer layer];
    progressLayer.strokeColor = btnColor.CGColor;
    progressLayer.fillColor = [UIColor whiteColor].CGColor;
    progressLayer.lineCap   = kCALineCapRound;
    progressLayer.lineJoin  = kCALineJoinBevel;
    progressLayer.lineWidth = 4.0;
   progressLayer.strokeEnd = 0.0;

    UIBezierPath *progressCircle = [UIBezierPath bezierPath];
    [progressCircle addArcWithCenter:_viewCenter radius:self.bounds.size.height/2 startAngle:-M_PI_2 endAngle:M_PI_2 * 3 clockwise:YES];
    progressLayer.path = progressCircle.CGPath;
    [backProgressLayer addSublayer:progressLayer];

    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    pathAnimation.duration = 3.0;
    pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0];
    pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
    pathAnimation.removedOnCompletion = NO;
    pathAnimation.fillMode = kCAFillModeForwards;
    [progressLayer addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
}

如果一切OK,你會(huì)看到下面效果圖:


part22222.gif
擴(kuò)展:

這里畫的進(jìn)度控制,你可以提供參數(shù)進(jìn)行控制。如,當(dāng)一個(gè)progressRatio達(dá)到20%時(shí)候,讓layer的bezierPath endAngle:這個(gè)參數(shù)為1/5 * (M_PI*2),或者動(dòng)畫的進(jìn)度定死,讓進(jìn)度條執(zhí)行幀動(dòng)畫先到20%,然后到60%,最后是否完成通過一個(gè)bool來控制,如果false則提示,如果true,則完成最后的環(huán)形合并。你可以自定義一個(gè)layer,提供一個(gè)方法傳入progressFloat,之后你在下載回調(diào)方法里面調(diào)用layer的drawRect:inContent方法,來重繪達(dá)到效果。

 這里自己實(shí)現(xiàn)下載進(jìn)度效果使用的是NSURLSession,了解請(qǐng)看源碼
3.第三部分設(shè)計(jì)實(shí)現(xiàn):

其實(shí)這里基本已經(jīng)效果已經(jīng)出來了,這里使用一個(gè)GCD延遲執(zhí)行。當(dāng)然你也可以使用NSTimer,CADisplayLink來實(shí)現(xiàn)。

Paste_Image.png

注意:這里的時(shí)間應(yīng)該是和上面的CABaseAnimation動(dòng)畫時(shí)間一致。
按鈕回復(fù)原來形狀

 - (void)expandLayerAnimation {   //方法實(shí)現(xiàn):
        [backProgressLayer removeFromSuperlayer];//移除之前圖層
        [_submitButton setShowSubmitButton];//顯示按鈕
        [_showLabel showLabelAnimation];//執(zhí)行對(duì)號(hào)動(dòng)畫
    
        //按鈕擴(kuò)充動(dòng)畫
        CABasicAnimation *anima = [CABasicAnimation animation];
        anima.duration = 1.;
        anima.keyPath = @"bounds";
        anima.toValue = [NSValue valueWithCGRect:_originRect];
        anima.removedOnCompletion = NO;
        anima.fillMode = kCAFillModeForwards;
       [self.submitButton.layer addAnimation:anima forKey:nil];
    }



- (void)showLabelAnimation {
    self.text = @"";
    [self creatCompleteAnimation];
    [UIView animateWithDuration:1.0 animations:^{
    self.alpha = 1;
    }];
}

- (void)creatCompleteAnimation {
    CGPoint centerPoint = (CGPoint){self.bounds.size.width/2, self.bounds.size.height/2 + 5};
    CGFloat margin = 10;

    CAShapeLayer *completeLayer = [CAShapeLayer layer];
    completeLayer.fillColor = [UIColor clearColor].CGColor;
    completeLayer.strokeColor = [UIColor whiteColor].CGColor;
   completeLayer.lineCap   = kCALineJoinRound;
    completeLayer.lineJoin  = kCALineJoinRound;
    completeLayer.lineWidth = 4;

    UIBezierPath *completePath = [UIBezierPath bezierPath];
    [completePath moveToPoint:CGPointMake(centerPoint.x - margin, centerPoint.y)];
    [completePath addLineToPoint:CGPointMake(centerPoint.x - 5, centerPoint.y + margin - 5)];
    [completePath addLineToPoint:CGPointMake(centerPoint.x + margin, centerPoint.y - margin)];
    completeLayer.path = completePath.CGPath;

    [self.layer addSublayer:completeLayer];

    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    pathAnimation.duration = 1.0;
    pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    pathAnimation.fromValue = [NSNumber numberWithFloat:0.0];
    pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
    pathAnimation.removedOnCompletion = NO;
    pathAnimation.fillMode = kCAFillModeForwards;
    [completeLayer addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
}

最終效果:

finishPart.gif

因?yàn)闀r(shí)間關(guān)系,代碼和功能實(shí)現(xiàn)還有很多地方?jīng)]有完善,這里只是希望大家互相分享能夠起到共同進(jìn)步的目的。歡迎指出不足,大家共同交流進(jìn)步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容