-
分析图:
- UnReadBubbleBtn.h
#import <UIKit/UIKit.h>
@interface UnReadBubbleBtn : UIButton
@end
- UnReadBubbleBtn.m
#import "UnReadBubbleBtn.h"
#define smallViewWH (self.bounds.size.width-1)
@interface UnReadBubbleBtn ()
@property(nonatomic, weak) UIView * smallV;
@property(nonatomic, weak) CAShapeLayer * shapeL;
@end
@implementation UnReadBubbleBtn
-(UIView *)smallV{
if (_smallV == nil) {
//添加小圆
UIView *smallV = [[UIView alloc] initWithFrame:CGRectMake(0, 0, smallViewWH, smallViewWH)];
smallV.center = self.center;
smallV.backgroundColor = self.backgroundColor;
smallV.layer.cornerRadius = smallV.bounds.size.width*0.5;
[self.superview insertSubview:smallV belowSubview:self];
_smallV = smallV;
}
return _smallV;
}
-(CAShapeLayer *)shapeL{
if (_shapeL == nil) {
CAShapeLayer * shapeL = [[CAShapeLayer alloc] init];
shapeL.fillColor = self.backgroundColor.CGColor;
[self.superview.layer insertSublayer:shapeL atIndex:0]; //superview.layer的子layer中的最底层
self.shapeL = shapeL;
}
return _shapeL;
}
-(void)layoutSubviews{
[super layoutSubviews];
[self setUp]; //初始化
}
-(void)awakeFromNib{
[self setUp]; //初始化
}
//初始化
-(void)setUp{
//设置圆角
self.layer.cornerRadius = self.bounds.size.width*0.5;
self.layer.masksToBounds = YES;
self.clipsToBounds = NO;
//添加手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self addGestureRecognizer:pan];
}
CGFloat dd = 0;
-(void)pan:(UIPanGestureRecognizer *)pan{
//由于计算中用到两圆圆心的值,所以将普通移位换成移动圆心(translate操作只会改变原来的frame,而不会改变圆心的值)
CGPoint tran = [pan translationInView:self];
CGPoint center = self.center;
center.x += tran.x;
center.y += tran.y;
self.center = center;
[pan setTranslation:CGPointZero inView:self];
//计算大圆与小圆的距离
CGFloat r = self.smallV.bounds.size.width*0.5;
// CGFloat R = self.bounds.size.width*0.5;
CGFloat x1 = self.smallV.center.x;
CGFloat y1 = self.smallV.center.y;
CGFloat x2 = self.center.x;
CGFloat y2 = self.center.y;
CGFloat d = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
NSLog(@"%f", d);
//缩小小圆半径
CGFloat n = 16.0; //数字越大,小圆半径减小越慢
if (d > dd) {
r = r - sqrt(tran.x*tran.x+tran.y*tran.y)/n;
}else{
r = r + sqrt(tran.x*tran.x+tran.y*tran.y)/n;
}
self.smallV.bounds = CGRectMake(0, 0, r*2, r*2);
self.smallV.layer.cornerRadius = r;
dd = d;
//描述路径
CGFloat cosθ = (y2-y1)/d;
CGFloat sinθ = (x2-x1)/d;
CGPoint A = CGPointMake(x1-r*cosθ, y1+r*sinθ);
CGPoint B = CGPointMake(x1+r*cosθ, y1-r*sinθ);
// CGPoint C = CGPointMake(x2+R*cosθ, y2-R*sinθ);
// CGPoint D = CGPointMake(x2-R*cosθ, y2+R*sinθ);
CGPoint C1 = CGPointMake(x2+(smallViewWH)*0.5*cosθ, y2-(smallViewWH)*0.5*sinθ);
CGPoint D1 = CGPointMake(x2-(smallViewWH)*0.5*cosθ, y2+(smallViewWH)*0.5*sinθ);
//CGPoint O = CGPointMake((self.center.x+self.smallV.center.x)*0.5, (self.center.y+self.smallV.center.y)*0.5);
CGPoint P1 = CGPointMake((B.x+C1.x)*0.5, (B.y+C1.y)*0.5);
CGPoint Q1 = CGPointMake((A.x+D1.x)*0.5, (A.y+D1.y)*0.5);
CGFloat k = (A.y-B.y)/(A.x-B.x);
CGFloat b = P1.y-k*P1.x;
CGFloat m = d/51.0; //分母越大,腰减小越慢
if (m < 1) {
m = 1;
}
CGFloat x = 2*r*cosθ;
CGFloat Px = Q1.x+x/m;
CGFloat Qx = P1.x-x/m;
CGPoint P = CGPointMake(Px, k*Px+b);
CGPoint Q = CGPointMake(Qx, k*Qx+b);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:A];
[path addLineToPoint:B];
[path addQuadCurveToPoint:C1 controlPoint:P];
[path addLineToPoint:D1];
[path addQuadCurveToPoint:A controlPoint:Q];
//描绘形状
if (self.smallV.hidden == NO) {
self.shapeL.path = path.CGPath;
}
//拉到一定长度,小圆与梯形都消失
if (r<=3) {
[self.shapeL removeFromSuperlayer];
self.smallV.hidden = YES;
}
//放手
if (pan.state == UIGestureRecognizerStateEnded){
if (self.smallV.hidden) { //小圆已经消失
if (d<=dd && d<=15) { //吸回原位
[self.shapeL removeFromSuperlayer];
self.center = self.smallV.center;
self.smallV.hidden = NO;
self.smallV.bounds = CGRectMake(0, 0, smallViewWH, smallViewWH);
self.smallV.layer.cornerRadius = self.smallV.bounds.size.width*0.5;
}else{ //爆炸
self.backgroundColor = [UIColor clearColor];
//大圆消失,播放爆炸动画
CGFloat bombW = 30;
CGFloat btnW = self.bounds.size.width;
UIImageView *imageV = [[UIImageView alloc] initWithFrame:CGRectMake(-(bombW-btnW)*0.5, -(bombW-btnW)*0.5, bombW, bombW)];
NSMutableArray *arr = [NSMutableArray array];
for (int i=1; i<=5; i++) {
UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:@"unreadBomb_%i",i]];
[arr addObject:img];
}
imageV.animationImages = arr;
imageV.animationDuration = 0.5;
imageV.animationRepeatCount = 1;
[imageV startAnimating];
[self addSubview:imageV];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self removeFromSuperview];
});
}
}else{
//删除路径,大圆弹回原位,小圆半径恢复原状
[self.shapeL removeFromSuperlayer];
[UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.2 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.center = self.smallV.center;
} completion:^(BOOL finished) {
self.smallV.bounds = CGRectMake(0, 0, smallViewWH, smallViewWH);
self.smallV.layer.cornerRadius = self.smallV.bounds.size.width*0.5;
}];
}
}
}
-(void)setHighlighted:(BOOL)highlighted{
}
@end
- ViewController.m(用代码加载气泡)
#import "ViewController.h"
#import "UnReadBubbleBtn.h"
@interface ViewController ()
@end
@implementation ViewController
-(void)viewDidLoad {
[super viewDidLoad];
UnReadBubbleBtn *btn = [[UnReadBubbleBtn alloc] initWithFrame:CGRectMake(100, 100, 16, 16)];
[btn setTitle:@"1" forState:UIControlStateNormal];
btn.backgroundColor = [UIColor redColor];
[btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[btn setFont:[UIFont systemFontOfSize:12]];
[self.view addSubview:btn];
}
@end
- 如果用 storyBoard 加载气泡按钮,记得将 AutoLayout 的勾去掉:
- 或者设置 translatesAutoresizingMaskIntoConstraints = YES;
#import "ViewController.h"
#import "NoReadBubbleBtn.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet NoReadBubbleBtn *unReadBtn;
@end
@implementation ViewController
-(void)viewDidLoad {
[super viewDidLoad];
self.unReadBtn.translatesAutoresizingMaskIntoConstraints = YES;
}
@end