最近看到一个UI设计效果图,自己尝试做了一下,代码最好还是去github上下载下来看吧:
源码下载地址:https://github.com/DMDavid/DMSubmitView
最近封装了一下,使用的话可以
pod 'DMSubmitView'
初步设计思路
这个submit动画可以分开为3部分设计:
-
按钮形变部分:
这部分按钮逐渐缩小自身的宽度,当自身宽度与高度相等时,则停止形变。
2.圆环部分:
这部分没啥好说的,现在APP上很多loading的效果,基本都是这样的。主要就是2个图层,下面图层是灰色的,上面的绿色图层作为subLayer覆盖在灰色图层上面。
3.完成部分:
这是这个效果的最后部分,即从一个圆逐渐还原成原来按钮的大小样式。然后上面做一个图层动画,画一个“对号”。
代码实现
清楚了基本思路后,就可以着手撸代码了。
你可以全部使用图层来实现,上面效果。但正所谓条条大路通罗马,这边提供的方法可做参考,如果有更好的方法设计,欢迎与我交流。
1.第一部分设计实现:
这里我仍然使用button作为点击的submit按钮,当然你也可以使用其他方法设计。接下来创建一个label来展示文字,为什么不使用button自带的textLabel?是因为如果使用button自带的label的话,执行动画后自带label大小也在改变,不是特别方便。当然,还是那句话,你也可以封装一个button。
之后,我们的层级关系应该如下图所示:
这里注意:蓝色为我们的submitView,红色为btn,浅蓝为label。
而button和label的父视图都为蓝色的submitView。
将按钮设计为圆角:
self.submitButton.layer.cornerRadius=self.submitButton.bounds.size.height/2;
self.submitButton.layer.masksToBounds = YES;
没什么好说的
在按钮的点击事件里面,去做一个让按钮宽度缩小的动画,使用UIView 的隐式动画,隐藏label,另外让按钮背景色逐渐变白,并给它添加边境宽度,颜色设为灰色。实现这个效果:
按钮点击事件:
- (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相同,否则动画会出现不圆润的情况。
如果一切顺利,你会看到下面效果:
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,你会看到下面效果图:
扩展:
这里画的进度控制,你可以提供参数进行控制。如,当一个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来实现。
注意:这里的时间应该是和上面的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"];
}
最终效果:
因为时间关系,代码和功能实现还有很多地方没有完善,这里只是希望大家互相分享能够起到共同进步的目的。欢迎指出不足,大家共同交流进步。