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;
}
推荐阅读
- 宽容谁
- 我要做大厨
- 增长黑客的海盗法则
- 画画吗()
- 2019-02-13——今天谈梦想()
- 远去的风筝
- 三十年后的广场舞大爷
- 叙述作文
- 20190302|20190302 复盘翻盘
- 学无止境,人生还很长