QQ粘性效果

2017-11-23  本文已影响15人  翻这个墙
##在BageView.m中
@interface BageView ()

//形状图层
@property (nonatomic, weak)  CAShapeLayer *shapeLayer;
//小圆控件
@property (nonatomic, weak) UIView *smallCircleView;

@end

@implementation BageView
//懒加载形状图层
- (CAShapeLayer *)shapeLayer
{
    if (_shapeLayer == nil) {
        // 可以根据路径生成图层
        CAShapeLayer *layer = [CAShapeLayer layer];

        layer.fillColor = [UIColor redColor].CGColor;

        [self.superview.layer insertSublayer:layer atIndex:0];//为什么要这样插入?

        _shapeLayer = layer;
    }

    return _shapeLayer;
}
//对象初始化
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {

        [self setUp];
    }
    return self;
}
//对象初始化
- (void)awakeFromNib
{
    // 初始化
    [self setUp];
}

// 初始化操作
- (void)setUp
{
##生成大圆view并设置属性(示例是在storyboard中创建,所以下面代码没有这步)
    // 默认背景颜色:红色
    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];
##根据手势拖动设置按钮移动
    // 添加pan手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];

    [self addGestureRecognizer:pan];
##在按钮原来的位置生成一个与按钮一模一样的小圆view
    // 添加一个小圆
    UIView *smallCircleView = [[UIView alloc] init];

    // 尺寸
    smallCircleView.frame = self.frame;

    // 颜色
    smallCircleView.backgroundColor = self.backgroundColor;

    // 设置圆角半径
    smallCircleView.layer.cornerRadius = self.layer.cornerRadius;

    // 添加到父控件
    [self.superview insertSubview:smallCircleView belowSubview:self];

    _smallCircleView = smallCircleView;
}
##根据手势拖动设置按钮移动
- (void)pan:(UIPanGestureRecognizer *)pan
{
    // 获取手指的偏移量
    CGPoint transP = [pan translationInView:self];

    // 修改形变,移动控件的位置
    // 修改transform并不会修改center
//    self.transform = CGAffineTransformTranslate(self.transform, transP.x, transP.y);
    CGPoint center = self.center;
    center.x += transP.x;
    center.y += transP.y;
    self.center = center;

    // 复位
    [pan setTranslation:CGPointZero inView:self];
##封装两圆中心距离计算方法(勾股定理)
    // 计算下两个圆心的距离
    CGFloat distance = [self distanceWithSmallView:_smallCircleView bigView:self];


##根据两圆中心距离及一定比例,设置小圆大小形变
    // 修改小圆的半径,根据圆心距离产生一个比例
    CGFloat smallR = self.bounds.size.width * 0.5 - distance / 10.0;

    _smallCircleView.bounds = CGRectMake(0, 0, smallR * 2 , smallR * 2);

    // 设置圆角半径
    _smallCircleView.layer.cornerRadius = smallR;

利用核心计算公式,计算并绘制直线和曲线路径,通过形状图层生成不规则矩形(计算方法封装,需注意两圆中心距离为0,直接return)
    // 计算不规则的矩形路径
    UIBezierPath *path = [self pathWithSmallView:_smallCircleView bigView:self];

    if (self.smallCircleView.hidden == NO) { // 当小圆没有显示的时候,就不需要描述不规则的矩形

        // 设置形状图层的路径
        self.shapeLayer.path = path.CGPath;
    }

##当两圆中心距离达到一定距离后,实现抽走效果(小圆同时隐藏)
    // 当圆心距离大于60的时候,吸附效果
    if (distance > 60) {
        // 隐藏小圆
        _smallCircleView.hidden = YES;

        // 不规则的矩形移除父控件
//        self.shapeLayer.hidden = YES;
        [self.shapeLayer removeFromSuperlayer];
    }

##如果松手位置在爆炸范围圈外,松开手通过核心动画或者imageView播放爆炸帧动画,并将大圆从父控件中移除

    if (pan.state == UIGestureRecognizerStateEnded) { // 手指抬起的时候,需要做判断
        if (distance > 60) {

            NSMutableArray *images = [NSMutableArray array];
            // 加载gif图片
            for (int i = 1; i <= 8; i++) {

                NSString *imageName = [NSString stringWithFormat:@"%d",i];

                UIImage *image = [UIImage imageNamed:imageName];
                [images addObject:image];
            }

            // 播放gif图片

            UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];

            // 设置动画数组
            imageView.animationImages = images;

            imageView.animationDuration = 1;

            [self addSubview:imageView];

            // 主动播放
            [imageView startAnimating];

            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self removeFromSuperview];
            });
##如果松手位置在爆炸范围圈内,通过弹簧效果使大圆重新回到原点,并显示小圆
        }else{ // 还原


            // 位置
            [UIView animateWithDuration:0.25 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                self.center = self.smallCircleView.center;

            } completion:^(BOOL finished) {

                // 小圆显示
                self.smallCircleView.hidden = NO;
            }];
        }
    }
}

利用核心计算公式,计算并绘制直线和曲线路径,通过形状图层生成不规则矩形(计算方法封装,需注意两圆中心距离为0,直接return))
// 计算不规则的矩形路径
- (UIBezierPath *)pathWithSmallView:(UIView *)smallView bigView:(UIView *)bigView
{
    // 计算圆心
    CGFloat d = [self distanceWithSmallView:smallView bigView:bigView];

    CGFloat y1 = smallView.center.y;
    CGFloat x1 = smallView.center.x;
    CGFloat r1 = smallView.layer.cornerRadius;

    CGFloat y2 = bigView.center.y;
    CGFloat x2 = bigView.center.x;
    CGFloat r2 = bigView.layer.cornerRadius;

    // 如果间距为0,不计算矩形路径
    if (d == 0) return nil;

    // cosθ
    CGFloat cosθ = (y2 - y1) / d;
    // sinθ
    CGFloat sinθ = (x2 - x1) / d;

    // A:
    CGPoint pointA = CGPointMake(x1 - r1 * cosθ, y1 + r1 * sinθ);
    // B:
    CGPoint pointB = CGPointMake(x1 + r1 * cosθ, y1 - r1 * sinθ);

    // C:
    CGPoint pointC = CGPointMake(x2 + r2 * cosθ, y2 - r2 * sinθ);

    // D:
    CGPoint pointD = CGPointMake(x2 - r2 * cosθ, y2 + r2 * sinθ);

    // O:
    CGPoint pointO = CGPointMake(pointA.x + d * 0.5 * sinθ, pointA.y + d * 0.5 * cosθ);

    // P:
    CGPoint pointP = CGPointMake(pointB.x + d * 0.5 * sinθ, pointB.y + d * 0.5 * cosθ);

    // 描述路径
    UIBezierPath *path = [UIBezierPath bezierPath];
    //  nan == null

    [path moveToPoint:pointA];

    // AB
    [path addLineToPoint:pointB];

    // BC
    // 绘制曲线
    [path addQuadCurveToPoint:pointC controlPoint:pointP];

    // CD
    [path addLineToPoint:pointD];

    // DA
    [path addQuadCurveToPoint:pointA controlPoint:pointO];

    return path;
}
##封装两圆中心距离计算方法(勾股定理)
// 计算两个控件的圆心距离
- (CGFloat)distanceWithSmallView:(UIView *)smallView bigView:(UIView *)bigView
{
    CGFloat offsetX = bigView.center.x - smallView.center.x;
    CGFloat offsetY = bigView.center.y - smallView.center.y;

    return sqrt(offsetX * offsetX + offsetY * offsetY);
}

- (void)setHighlighted:(BOOL)highlighted{}

@end


##VC.m中,要取消autoMask转化为自动布局,否则位移会有问题

- (void)viewDidLoad {
    [super viewDidLoad];

    // 取消autoMask转化为自动布局
    self.view.translatesAutoresizingMaskIntoConstraints = NO;
}
上一篇 下一篇

猜你喜欢

热点阅读