QQ粘性效果

  • 效果:拖动信息提示数目按钮,感觉像是在拉伸按钮,当拖动到一定范围,按钮(小圆被抽出),松开手小圆会爆炸。如果抽出小圆后,将小圆移回之前的位置,小圆会被恢复
  • 核心计算公式:(见图中A、B、C、D、O、P点公式)
  • 步骤:
    1. 生成大圆view并设置属性(示例是在storyboard中创建,所以下面代码没有这步)
    • 根据手势拖动设置按钮移动
    • 在按钮原来的位置生成一个与按钮一模一样的小圆view
    • 封装两圆中心距离计算方法(勾股定理)
    • 根据两圆中心距离及一定比例,设置小圆大小形变
    • 利用核心计算公式,计算并绘制直线和曲线路径,通过形状图层生成不规则矩形(计算方法封装,需注意两圆中心距离为0,直接return)
    • 当两圆中心距离达到一定距离后,实现抽走效果(小圆同时隐藏)
    • 如果松手位置在爆炸范围圈外,松开手通过核心动画或者imageView播放爆炸帧动画,并将大圆从父控件中移除
    • 如果松手位置在爆炸范围圈内,通过弹簧效果使大圆重新回到原点,并显示小圆
    • VC.m中,要取消autoMask转化为自动布局,否则位移会有问题
      [图片上传失败...(image-220148-1511405997537)]
##在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;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • QQ粘性效果 实现思路: 1.自定义大圆控件(UIButton)可以显示背景图片,和文字 2.让大圆控件随着手指移...
    SoManyDumb阅读 237评论 0 0
  • 制作步骤 1.自定义按钮控件 设置背景颜色,设置layer的cornerRadius属性,添加手势,重写setHi...
    冲破茧缚阅读 1,329评论 1 7
  • 动画分析 当前控件既可以显示图片,有可以显示文字,那么我们就可以通过按钮来最为当前的控件. 当拖动控件,当前控件尺...
    亡灵诅咒阅读 485评论 0 2
  • 整体思路: 手指移动,按钮跟着移动.按钮跟着手指移动.移动时底部有一个圆, 根据上面的大圆按钮拖动的距离,小圆的半...
    翻滚的企鹅阅读 677评论 0 0
  • 图片折叠效果 1.如何制作图片折叠效果? `把一张图片分成两部分显示,上面一部分,下面一部分,折叠上面部分的内容。...
    Hevin_Chen阅读 479评论 0 1