首先来看看实现后的效果吧
实现思路
其实这个按钮的实现时分简单,我的思路是:
- 用UIView + UITapGestureRecognizer 来模拟实现一个按钮的效果
- 记录每次手指点击的位置,以该位置为圆心用CALayer画圆
- 采用block回调实现点击事件
- 采用CAAnimationGroup来实现组合动画(涟漪效果)
- 最后在CAAnimationDelegate中- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;方法中消除动画。
是不是很简单?
下面我们来看下代码实现
//在ZYCRippleButton.h文件中
#import <UIKit/UIKit.h>
typedef void (^ZYCRippleButtonBlock)(void);
@interface ZYCRippleButton : UIView
//用于模拟button的tittle
@property (nonatomic, strong) UILabel *textLabel;
//“涟漪”颜色
@property (nonatomic, strong) UIColor *rippleColor;
//“涟漪”的粗细
@property (nonatomic, assign) NSUInteger rippleLineWidth;
//点击回调block
@property (nonatomic, copy) ZYCRippleButtonBlock rippleBlock;
- (void) setButtonTittle:(NSString *)tittle;
- (void) setButtonTittleColor:(UIColor *)tColor;
- (void) setButtonBackgroundColor:(UIColor *)bgColor;
- (void) setButtonTittle:(NSString *)tittle withTittleColor:(UIColor *)tColor;
- (void) setButtonTittle:(NSString *)tittle withTittleColor:(UIColor *)tColor backgroundColor:(UIColor *)bgColor;
@end
//在ZYCRippleButton.m文件中
#import "ZYCRippleButton.h"
const CGFloat ZYCRippleInitialRaius = 20;
@interface ZYCRippleButton()<CAAnimationDelegate>
@end
@implementation ZYCRippleButton
- (instancetype)initWithFrame:(CGRect)frame{
if (self == [super initWithFrame:frame]){
[self initZYCRippleButton];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self == [super initWithCoder:aDecoder]){
[self initZYCRippleButton];
}
return self;
}
#pragma mark - init
- (void)initZYCRippleButton{
//模拟按钮点击效果
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapped:)];
[self addGestureRecognizer:tap];
//初始化label
self.textLabel = [[UILabel alloc]initWithFrame:self.bounds];
self.textLabel.backgroundColor = [UIColor clearColor];
self.textLabel.textColor = [UIColor blackColor];
self.textLabel.textAlignment = NSTextAlignmentCenter;
self.textLabel.text = @"button";
[self addSubview:_textLabel];
self.backgroundColor = [UIColor lightGrayColor];
self.clipsToBounds = YES;
}
- (void) setButtonTittle:(NSString *)tittle{
self.textLabel.text = tittle;
}
- (void) setButtonTittleColor:(UIColor *)tColor{
self.textLabel.textColor = tColor;
}
- (void) setButtonBackgroundColor:(UIColor *)bgColor{
self.backgroundColor = bgColor;
}
- (void) setButtonTittle:(NSString *)tittle withTittleColor:(UIColor *)tColor{
[self setButtonTittle:tittle withTittleColor:tColor backgroundColor:nil];
}
- (void) setButtonTittle:(NSString *)tittle withTittleColor:(UIColor *)tColor backgroundColor:(UIColor *)bgColor{
if (tittle){
self.textLabel.text = tittle;
}
if (tColor){
self.textLabel.textColor = tColor;
}
if (bgColor){
self.backgroundColor = bgColor;
}
}
#pragma mark - tapped
- (void)tapped:(UITapGestureRecognizer *)tap{
//获取所点击的那个点
CGPoint tapPoint = [tap locationInView:self];
//创建涟漪
CAShapeLayer *rippleLayer = nil;
CGFloat buttonWidth = self.frame.size.width;
CGFloat buttonHeight = self.frame.size.height;
CGFloat bigBoard = buttonWidth >= buttonHeight ? buttonWidth : buttonHeight;
CGFloat smallBoard = buttonWidth <= buttonHeight ? buttonWidth : buttonHeight;
CGFloat rippleRadiius = smallBoard/2 <= ZYCRippleInitialRaius ? smallBoard/2 : ZYCRippleInitialRaius;
CGFloat scale = bigBoard / rippleRadiius + 0.5;
rippleLayer = [self createRippleLayerWithPosition:tapPoint rect:CGRectMake(0, 0, rippleRadiius * 2, rippleRadiius * 2) radius:rippleRadiius];
[self.layer addSublayer:rippleLayer];
//layer动画
CAAnimationGroup *rippleAnimationGroup = [self createRippleAnimationWithScale:rippleRadiius duration:1.5f];
//使用KVC消除layer动画以防止内存泄漏
[rippleAnimationGroup setValue:rippleLayer forKey:@"rippleLayer"];
[rippleLayer addAnimation:rippleAnimationGroup forKey:nil];
rippleLayer.delegate = self;
}
#pragma mark - createRippleLayer && CAAnimationGroup
- (CAShapeLayer *)createRippleLayerWithPosition:(CGPoint)position rect:(CGRect)rect radius:(CGFloat)radius{
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = [self createPathWithRadius:rect radius:radius];
layer.position = position;
layer.bounds = CGRectMake(0, 0, radius * 2, radius * 2);
layer.fillColor = self.rippleColor ? self.rippleColor.CGColor : [UIColor whiteColor].CGColor;
layer.opacity = 0;
layer.lineWidth = self.rippleLineWidth ? self.rippleLineWidth : 1;
return layer;
}
- (CAAnimationGroup *)createRippleAnimationWithScale:(CGFloat)scale duration:(CGFloat)duration{
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(scale, scale, 1)];
CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
alphaAnimation.fromValue = @0.5;
alphaAnimation.toValue = @0;
CAAnimationGroup *animation = [CAAnimationGroup animation];
animation.animations = @[scaleAnimation, alphaAnimation];
animation.delegate = self;
animation.duration = duration;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
return animation;
}
- (CGPathRef)createPathWithRadius:(CGRect)frame radius:(CGFloat)radius{
return [UIBezierPath bezierPathWithRoundedRect:frame cornerRadius:radius].CGPath;
}
#pragma mark - CAAnimationDelegate
- (void)animationDidStart:(CAAnimation *)anim{
if (self.rippleBlock){
self.rippleBlock();
}
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
CALayer *layer = [anim valueForKey:@"rippleLayer"];
if (layer) {
[layer removeFromSuperlayer];
}
}
@end
以上就完成了一个自定义的模仿安卓Material Design按钮点击的iOS版按钮
让我们来测试下吧
@interface ViewController ()
@property (nonatomic, strong) ZYCRippleButton *rippleButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.rippleButton = [[ZYCRippleButton alloc]initWithFrame:CGRectMake((self.view.frame.size.width - 300)/2, 100 , 300, 100)];
self.rippleButton.rippleLineWidth = 1;
self.rippleButton.rippleColor = [UIColor whiteColor];
[self.rippleButton setButtonTittle:@"测试按钮1" withTittleColor:[UIColor whiteColor] backgroundColor:[UIColor colorWithRed:36.0f/255.0f green:188.0f/255.0f blue:255.0f/255.0f alpha:0.5]];
__block typeof(NSInteger) tapNum = 0;
__block typeof(self) bself = self;
self.rippleButton.rippleBlock = ^(void){
[bself.rippleButton setButtonTittle:[NSString stringWithFormat:@"点击第%ld次",(long)tapNum++]];
};
[self.view addSubview:_rippleButton];
}
- (void)onClick:(NSInteger)i{
[self.rippleButton setButtonTittle:[NSString stringWithFormat:@"点击%ld次",(long)i++]];
}
@end
下面附上项目github地址
点我下载(喜欢的话记得给我个star哦_)