zl程序教程

您现在的位置是:首页 >  工具

当前栏目

一个类似于QQ语音聊天时的拖拽移动悬浮小球

QQ 一个 移动 语音 聊天 类似 拖拽 悬浮
2023-09-11 14:18:58 时间

闲来无事,分享一个最近在某个地方借鉴的一个demo(原谅我真的忘了在哪里看到的了,不然也就贴地址了)这个demo的逻辑思路并不是很难,推敲一下,很快就能理解,只是觉得这样的一个组合控件用起来蛮能增色自己的APP的,所以也就记下了。

 

先给你们看一下效果图。

 

 

这里的悬浮小球其实是一个组合控件,可以在上面加上其他效果。之后我会上传demo。如果要做成QQ语音的那种,可以把图片移除,换上一个label,在label上加上时间久可以了,用的时候,可以直接把这个类拖进工程,直接加到想要添加的仕途上就可以啦。

 

这个demo我也是学习过程中做了很多注释,基本上都应该能看懂,还是一样不多解释,看注释看代码。^_^

 

这是还是说一下具体的实现功能吧:

 

第一个:是营造一个呼吸的效果,这个呼吸效果实际上只是一个假象,UI控件哪有什么呼吸的嘛,都是自己YY的。这个呼吸效果实际上是通过动画效果改变Alpha,不断循环,达到一致循环改变Alpha,感觉就像是在呼吸一样。

 

第二个:是图片效果,这里是采用了UIImageRenderingModeAlwaysTemplate,不懂得同学可以查一下,不过我demo里面也有部分解释。这里主要是忽略的图片的颜色,设置成自己想要的颜色(这里的图片原本是黑色的,我通过这个枚举,让他的颜色变成了白色)

 

第三个:是移动,这里是通过-(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event , -(void)touchesMoved:(NSSet )touches withEvent:(UIEvent )event以及 – (void)touchesEnded:(NSSet )touches withEvent:(UIEvent )event 这三个方法来实现移动的,大致就是触摸时取出触摸点坐标,然后作为悬浮小球的center,实现了小球的移动。

 

//将触摸点赋值给touchView的中心点 也就是根据触摸的位置实时修改view的位置

 

    self.center = [startTouch locationInView:self.superview];

 

还有一个注意的是在开始移动之前一定要把之前的效果给移除掉,不然会很奇葩的,不信你也可以试一试。

 

// 移除之前的所有行为

    // 如果不移除,之前移动所触发的物理吸附事件就会一直执行

    [self.animator removeAllBehaviors];

 

第四:就是计算计算距离最近的边缘 吸附到边缘停靠,具体的计算就不说了,因为太繁琐了,没有想着优化,看看啥时候闲下来再来看看能不能优化吧。主要就是考虑的情况稍微多了点,但是逻辑上还是很清晰的。这里有一个吸附的物理行为,其实也就是一个动画效果,直接加上就好了,也不是很复杂。

 

好的,具体的都解释完了。上代码!!!,突然发现并不能传文件,好吧,赋值过来看看吧

 

这是.h里面的代码

 

这里只是给外界留了个借口,这些属性也可以.m里面,我只是在外界用到了这个而已。

 

#import

 

typedef void (^DownLoadBlock) ();

@interface xuanfuwu : UIView

 

 

 

@property (nonatomic ,assign) CGPoint startPoint;//触摸起始点

 

@property (nonatomic ,assign) CGPoint endPoint;//触摸结束点

 

@property (nonatomic ,copy) DownLoadBlock downLoadBlock;

@end

 

这是.m里面的代码

 

//主题颜色

 

#import "AppDelegate.h"

#import "xuanfuwu.h"

 

 

 

 

#define MAINCOLOER [UIColor colorWithRed:105/255.0 green:149/255.0 blue:246/255.0 alpha:1]

 

 

#define kDownLoadWidth 60

 

#define kOffSet kDownLoadWidth / 2

 

 

@interface xuanfuwu ()

 

@property (nonatomic , retain ) UIView *backgroundView;//背景视图

 

@property (nonatomic , retain ) UIImageView *imageView;//图片视图

 

@property (nonatomic , retain ) UIDynamicAnimator *animator;//物理仿真动画

@end

 

@implementation xuanfuwu

 

//初始化

 

- (instancetype)initWithFrame:(CGRect)frame{

 

    // 设置 xuanfuwu 这个视图的大小 宽高都是60

    frame.size.width = kDownLoadWidth;

 

    frame.size.height = kDownLoadWidth;

 

 

    if (self = [super initWithFrame:frame]) {

 

 

        //初始化背景视图

 

        _backgroundView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame))];

 

        // 让初始化背景变成圆

        _backgroundView.layer.cornerRadius = _backgroundView.frame.size.width / 2;

 

//        clipsToBounds

//        是指视图上的子视图,如果超出父视图的部分就截取掉,

//        masksToBounds

//        却是指视图的图层上的子图层,如果超出父图层的部分就截取掉

        _backgroundView.clipsToBounds = YES;

 

        // 设置颜色和透明度

        _backgroundView.backgroundColor = [MAINCOLOER colorWithAlphaComponent:0.7];

 

        _backgroundView.userInteractionEnabled = NO;

 

        [self addSubview:_backgroundView];

 

        //初始化图片背景视图

        // 比背景视图稍微小一点,显示出呼吸的效果

 

        UIView * imageBackgroundView = [[UIView alloc]initWithFrame:CGRectMake(5, 5, CGRectGetWidth(self.frame) - 10, CGRectGetHeight(self.frame) - 10)];

        // 变圆

        imageBackgroundView.layer.cornerRadius = imageBackgroundView.frame.size.width / 2;

 

        imageBackgroundView.clipsToBounds = YES;

 

        imageBackgroundView.backgroundColor = [MAINCOLOER colorWithAlphaComponent:0.8f];

 

        imageBackgroundView.userInteractionEnabled = NO;

 

        [self addSubview:imageBackgroundView];

 

 

 

        //初始化图片

 

        _imageView = [[UIImageView alloc]initWithImage:[[UIImage imageNamed:@"1.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];

 

 

        // UIImageRenderingModeAutomatic  // 根据图片的使用环境和所处的绘图上下文自动调整渲染模式。

        // UIImageRenderingModeAlwaysOriginal   // 始终绘制图片原始状态,不使用Tint Color。

        // UIImageRenderingModeAlwaysTemplate   // 始终根据Tint Color绘制图片,忽略图片的颜色信息。

 

 

        _imageView.tintColor = [UIColor whiteColor];

 

        _imageView.frame = CGRectMake(0, 0, 30, 30);

 

        _imageView.center = CGPointMake(kDownLoadWidth / 2 , kDownLoadWidth / 2);

 

        [self addSubview:_imageView];

 

 

 

        //将正方形的view变成圆形

 

        self.layer.cornerRadius = kDownLoadWidth / 2;

 

 

        //开启呼吸动画

 

        [self HighlightAnimation];

 

    }

 

    return self;

 

}

 

 

// 触摸事件的触摸点

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

 

    //得到触摸点

 

    UITouch *startTouch = [touches anyObject];

 

    //返回触摸点坐标

 

    self.startPoint = [startTouch locationInView:self.superview];

 

    // 移除之前的所有行为

    // 如果不移除,之前移动所触发的物理吸附事件就会一直执行

    [self.animator removeAllBehaviors];

 

 

}

 

//触摸移动

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{

 

    //得到触摸点

    // 这里只有一个手指,移动的坐标只有一个

    UITouch *startTouch = [touches anyObject];

 

    //将触摸点赋值给touchView的中心点 也就是根据触摸的位置实时修改view的位置

 

    self.center = [startTouch locationInView:self.superview];

 

}

 

//结束触摸

 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{

 

    //得到触摸结束点

 

    UITouch *endTouch = [touches anyObject];

 

    //返回触摸结束点

    // 这个方法是指:触摸的点时在哪个视图,xuanfuwu 是加在viewController上的,所以用superView

    self.endPoint = [endTouch locationInView:self.superview];

 

    //判断是否移动了视图 (误差范围5)

 

    CGFloat errorRange = 5;

 

    if (( self.endPoint.x - self.startPoint.x >= -errorRange && self.endPoint.x - self.startPoint.x = -errorRange && self.endPoint.y - self.startPoint.y  bottomRange ? bottomRange : topRange;//获取上下最小距离

 

        CGFloat minRangeLR = leftRange > rightRange ? rightRange : leftRange;//获取左右最小距离

 

        CGFloat minRange = minRangeTB > minRangeLR ? minRangeLR : minRangeTB;//获取最小距离

 

 

        //判断最小距离属于上下左右哪个方向 并设置该方向边缘的point属性

 

        CGPoint minPoint;

 

        if (minRange == topRange) {

 

            //上

 

            endX = endX - kOffSet  superwidth ? superwidth - kOffSet : endX;

 

            minPoint = CGPointMake(endX , 0 + kOffSet);

 

        } else if(minRange == bottomRange){

 

            //下

 

            endX = endX - kOffSet  superwidth ? superwidth - kOffSet : endX;

 

            minPoint = CGPointMake(endX , superheight - kOffSet);

 

        } else if(minRange == leftRange){

 

            //左

 

            endY = endY - kOffSet  superheight ? superheight - kOffSet : endY;

 

            minPoint = CGPointMake(0 + kOffSet , endY);

 

        } else if(minRange == rightRange){

 

            //右

 

            endY = endY - kOffSet  superheight ? superheight - kOffSet : endY;

 

            minPoint = CGPointMake(superwidth - kOffSet , endY);

 

        }

 

 

        //添加吸附物理行为

 

        UIAttachmentBehavior *attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:self attachedToAnchor:minPoint];

 

        // 吸附行为中的两个吸附点之间的距离,通常用这个属性来调整吸附的长度,可以创建吸附行为之后调用。系统基于你创建吸附行为的方法来自动初始化这个长度

        [attachmentBehavior setLength:0];

 

        // 阻尼,相当于回弹过程中额阻力效果

        [attachmentBehavior setDamping:0.1];

 

        // 回弹的频率

        [attachmentBehavior setFrequency:3];

 

        [self.animator addBehavior:attachmentBehavior];

 

 

    }

 

 

}

 

#pragma mark ---UIDynamicAnimatorDelegate

 

- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator{

 

 

 

}

 

 

 

#pragma mark ---LazyLoading

 

- (UIDynamicAnimator *)animator

{

 

    if (!_animator) {

 

        // 创建物理仿真器(ReferenceView : 仿真范围)

 

        _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.superview];

 

        //设置代理

 

        _animator.delegate = self;

 

    }

 

    return _animator;

 

}

 

 

 

#pragma mark ---BreathingAnimation 呼吸动画

 

// 实质就是一个循环动画,透明度来回改变

- (void)HighlightAnimation{

 

    // 修改block块里面的值

    __block typeof(self) Self = self;

 

    [UIView animateWithDuration:1.5f animations:^{

 

        Self.backgroundView.backgroundColor = [Self.backgroundView.backgroundColor colorWithAlphaComponent:0.1f];

 

    } completion:^(BOOL finished) {

 

        [Self DarkAnimation];

 

    }];

 

}

 

- (void)DarkAnimation{

 

    __block typeof(self) Self = self;

 

    [UIView animateWithDuration:1.5f animations:^{

 

        Self.backgroundView.backgroundColor = [Self.backgroundView.backgroundColor colorWithAlphaComponent:0.6f];

 

    } completion:^(BOOL finished) {

 

        [Self HighlightAnimation];

 

    }];

 

}