QQ粘性布局

【QQ粘性布局】在一个storyBoard里面拖一个UIButton进去,然后新建一个自定义UIBUtton类

@interface LLButton () /** 圆小控件 */ @property (nonatomic, weak) UIView *smallCircle; /** <#annotation#> */ @property (nonatomic, strong) CAShapeLayer *shapeLayer; @end@implementation LLButton#pragma mark - Lazy load - (UIView *)smallCircle { if (!_smallCircle) { UIView *smallCircle = [[UIView alloc] init]; smallCircle.backgroundColor = self.backgroundColor; [self.superview insertSubview:smallCircle belowSubview:self]; _smallCircle = smallCircle; } return _smallCircle; }- (CAShapeLayer *)shapeLayer { if (!_shapeLayer) { CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.fillColor = self.backgroundColor.CGColor; [self.superview.layer insertSublayer:shapeLayer below:self.layer]; _shapeLayer = shapeLayer; } return _shapeLayer; }#pragma mark - 系统初始化// 按钮定义的时候要在初始方法中,把它的基本属性设置好.在开始加载的时候设置. // 基本属性包括颜色,圆角,文字颜色,大小. - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self __initSubviews]; } return self; }- (void)awakeFromNib { [super awakeFromNib]; [self __initSubviews]; }#pragma mark - 初始化子控件 - (void)__initSubviews { self.backgroundColor = [UIColor redColor]; self.layer.cornerRadius = self.bounds.size.width * 0.5; self.titleLabel.font = [UIFont systemFontOfSize:12]; [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; self.smallCircle.layer.cornerRadius = self.layer.cornerRadius; self.smallCircle.frame = self.frame; UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; [self addGestureRecognizer:pan]; }// 屏蔽按钮的高亮状态 - (void)setHighlighted:(BOOL)highlighted {}#pragma mark - 自定义方法 #pragma mark - 手势触发 #define MaxDistance 90 - (void)pan:(UIPanGestureRecognizer *)pan { // 移动 CGPoint transP = [pan translationInView:self]; CGPoint center = self.center; center.x += transP.x; center.y += transP.y; self.center = center; // 要进行复位,相对于上一次 [pan setTranslation:CGPointZero inView:self]; // 设置小圆变化的值 CGFloat distance = [self distanceWithSmallCircle:self.smallCircle bigCircle:self]; // 这里是取出小圆最初的宽度,由于每次拖动的时候都会去修改小圆的宽高.所以这里不能直接用小圆的宽度 // 这里用的是大圆的宽度,开始小圆和大圆的宽度是一样的. // 大圆在移动时,大圆的宽高没有发现变化,所以可以拿到大圆的宽高 CGFloat smallR = self.bounds.size.width*0.5; smallR = smallR - distance/10.0; if (smallR < 0) { smallR = 0; } self.smallCircle.bounds = CGRectMake(0, 0, smallR*2, smallR*2); self.smallCircle.layer.cornerRadius = smallR; // 判断圆心距是否大于规定的距离 if (distance > MaxDistance) { // 大于的话隐藏小圆,移除不规则矩形self.smallCircle.hidden = YES; [self.shapeLayer removeFromSuperlayer]; self.shapeLayer = nil; } else if (self.smallCircle.hidden == NO && distance > 0) { self.shapeLayer.path = [self pathWithSmallCircleView:self bigCircle:self.smallCircle].CGPath; }// 爆炸或还原 if (pan.state == UIGestureRecognizerStateBegan) { NSLog(@"%@", NSStringFromCGRect(self.frame)); }if (pan.state == UIGestureRecognizerStateEnded) { if (distance > MaxDistance) { // 动画的爆炸效果 UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds]; NSLog(@"%@", NSStringFromCGRect(self.frame)); NSMutableArray *imageArr = [NSMutableArray array]; for (int i = 0; i < 9; i++) { UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"explode%d", i]]; [imageArr addObject:image]; } imageView.animationImages = imageArr; imageView.animationDuration = 0.5; imageView.animationRepeatCount = 1; [imageView startAnimating]; [self addSubview:imageView]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 移除控件 [self removeFromSuperview]; }); } else { // 回弹 [self.shapeLayer removeFromSuperlayer]; self.shapeLayer = nil; [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.2 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{ self.center = self.smallCircle.center; } completion:^(BOOL finished) { self.smallCircle.hidden = NO; }]; } } }#pragma mark - 获取圆心距离 - (CGFloat)distanceWithSmallCircle:(UIView *)smallView bigCircle:(UIView *)bigCircle { CGFloat offsetX = bigCircle.center.x - smallView.center.x; CGFloat offsetY = bigCircle.center.y - smallView.center.y; CGFloat distance = sqrt(offsetX*offsetX + offsetY*offsetY); return distance; }#pragma mark - 获取贝塞尔曲线 - (UIBezierPath *)pathWithSmallCircleView:(UIView *)smallView bigCircle:(UIView *)bigCircle { // 小圆 CGFloat x1 = smallView.center.x; CGFloat y1 = smallView.center.y; CGFloat r1 = smallView.bounds.size.width*0.5; // 大圆 CGFloat x2 = bigCircle.center.x; CGFloat y2 = bigCircle.center.y; CGFloat r2 = bigCircle.bounds.size.width*0.5; CGFloat d = [self distanceWithSmallCircle:smallView bigCircle:bigCircle]; CGFloat cosθ = (y2 - y1)/d; CGFloat sinθ = (x2 - x1)/d; //A B C D O P CGPoint A = CGPointMake(x1-r1*cosθ, y1+r1*sinθ); CGPoint B = CGPointMake(x1+r1*cosθ, y1-r1*sinθ); CGPoint C = CGPointMake(x2+r2*cosθ, y2-r2*sinθ); CGPoint D = CGPointMake(x2-r2*cosθ, y2+r2*sinθ); CGPoint O = CGPointMake(A.x+d/2*sinθ, A.y+d/2*cosθ); CGPoint P = CGPointMake(B.x+d/2*sinθ, B.y+d/2*cosθ); //绘制路径 UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:A]; [path addLineToPoint:B]; [path addQuadCurveToPoint:C controlPoint:P]; [path addLineToPoint:D]; [path addQuadCurveToPoint:A controlPoint:O]; return path; }

    推荐阅读