iOS 一个提交按钮形变动画设计思路

最近看到一个UI设计效果图,自己尝试做了一下,代码最好还是去github上下载下来看吧:

1504118-622ed4419554c027.gif

源码下载地址:https://github.com/DMDavid/DMSubmitView
最近封装了一下,使用的话可以

pod 'DMSubmitView'

初步设计思路

这个submit动画可以分开为3部分设计:

  1. 按钮形变部分:


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

这部分按钮逐渐缩小自身的宽度,当自身宽度与高度相等时,则停止形变。

2.圆环部分:


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

这部分没啥好说的,现在APP上很多loading的效果,基本都是这样的。主要就是2个图层,下面图层是灰色的,上面的绿色图层作为subLayer覆盖在灰色图层上面。

3.完成部分:


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

这是这个效果的最后部分,即从一个圆逐渐还原成原来按钮的大小样式。然后上面做一个图层动画,画一个“对号”。

代码实现

清楚了基本思路后,就可以着手撸代码了。
你可以全部使用图层来实现,上面效果。但正所谓条条大路通罗马,这边提供的方法可做参考,如果有更好的方法设计,欢迎与我交流。

1.第一部分设计实现:

这里我仍然使用button作为点击的submit按钮,当然你也可以使用其他方法设计。接下来创建一个label来展示文字,为什么不使用button自带的textLabel?是因为如果使用button自带的label的话,执行动画后自带label大小也在改变,不是特别方便。当然,还是那句话,你也可以封装一个button。
之后,我们的层级关系应该如下图所示:


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

这里注意:蓝色为我们的submitView,红色为btn,浅蓝为label。
而button和label的父视图都为蓝色的submitView。

将按钮设计为圆角:

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

没什么好说的

在按钮的点击事件里面,去做一个让按钮宽度缩小的动画,使用UIView 的隐式动画,隐藏label,另外让按钮背景色逐渐变白,并给它添加边境宽度,颜色设为灰色。实现这个效果:


Paste_Image.png

按钮点击事件:

 - (void)submitBtnClick:(UIButton *)submitBtn {
    //缩小动画
    [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]; 
}];

}

//缩放动画
- (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隐式动画的duration的时间应该和scaleLayerAnimation的duration相同,否则动画会出现不圆润的情况。

如果一切顺利,你会看到下面效果:


part1111.gif
2.第二部分设计实现:

这里我们已经完成了让按钮变成一个圆环啦。接下来,其实你也可以直接使用button的图层,在它上面创建一个绿色环形图层,执行一个动画,做出loading的效果。当然实现的方法很多,我这里使用的是让这个button隐藏,自己重新创建一个灰色图层,它的大小样式都和button形变后的效果一样,只不过隐式动画比较快,难以区分,我们在动画完成block回调里面创建这个图层。

 - (void)submitBtnClick:(UIButton *)submitBtn {
    //缩小动画
    [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 - 在这里实现完成block后的操作!

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

}

//进度环动画
-(void) drawProgressLayer {  //方法实现:

    _viewCenter = (CGPoint){self.bounds.size.width/2, self.bounds.size.height/2};
    //1. 背景环    
    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. 圆环
    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,你会看到下面效果图:


part22222.gif
扩展:

这里画的进度控制,你可以提供参数进行控制。如,当一个progressRatio达到20%时候,让layer的bezierPath endAngle:这个参数为1/5 * (M_PI*2),或者动画的进度定死,让进度条执行帧动画先到20%,然后到60%,最后是否完成通过一个bool来控制,如果false则提示,如果true,则完成最后的环形合并。你可以自定义一个layer,提供一个方法传入progressFloat,之后你在下载回调方法里面调用layer的drawRect:inContent方法,来重绘达到效果。

 这里自己实现下载进度效果使用的是NSURLSession,了解请看源码
3.第三部分设计实现:

其实这里基本已经效果已经出来了,这里使用一个GCD延迟执行。当然你也可以使用NSTimer,CADisplayLink来实现。

Paste_Image.png

注意:这里的时间应该是和上面的CABaseAnimation动画时间一致。
按钮回复原来形状

 - (void)expandLayerAnimation {   //方法实现:
        [backProgressLayer removeFromSuperlayer];//移除之前图层
        [_submitButton setShowSubmitButton];//显示按钮
        [_showLabel showLabelAnimation];//执行对号动画
    
        //按钮扩充动画
        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

因为时间关系,代码和功能实现还有很多地方没有完善,这里只是希望大家互相分享能够起到共同进步的目的。欢迎指出不足,大家共同交流进步。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,475评论 4 372
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,744评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,101评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,732评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,141评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,049评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,188评论 2 320
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,965评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,716评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,867评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,341评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,663评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,376评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,200评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,990评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,179评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,979评论 2 279

推荐阅读更多精彩内容