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

闲来无事,分享一个最近在某个地方借鉴的一个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];

}];

}

时间: 2024-08-09 10:37:04

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

实现一个简单的语音聊天室(多人语音聊天系统)

多人语音聊天,或语音聊天室,是即时通信应用中常见的功能之中的一个,比方,QQ的语音讨论组就是我们用得比較多的. 本文将基于最新版本号的OMCS(V3.5)实现一个简单的语音聊天室,让多个人能够进入同一个房间进行语音沟通.当然,在此之前,您必须对OMCS有所了解,而且已经阅读.理解了OMCS 开发手冊(08) -- 多人语音/视频这篇文章的内容.先看看Demo执行效果截图:        从左到右的三张图各自是:登录界面.语音聊天室的主界面.标注了各个控件的主界面. 一. C/S结构 非常明显,我

实现一个简单的语音聊天室(源码)

语音聊天室,或多人语音聊天,是即时通信应用中常见的功能之一,比如,QQ的语音讨论组就是我们用得比较多的. 这篇文章将实现一个简单的语音聊天室,让多个人可以进入同一个房间进行语音沟通.先看运行效果截图:         从左到右的三张图分别是:登录界面.语音聊天室的主界面.标注了各个控件的主界面. (如果觉得界面太丑,没关系,后面下载源码后,你可以自己美化~~) 一. C/S结构 很明显,我这个语音聊天室采用的是C/S结构,整个项目结构相对比较简单,如下所示: 该项目的底层是基于OMCS构建的.这

Qt无边框窗体-最大化时支持拖拽还原

目录 一.概述 二.效果展示 三.demo制作 1.设计窗体 2.双击放大 四.拖拽 五.相关文章 原文链接:Markdown模板 一.概述 用Qt进行开发界面时,既想要实现友好的用户交互又想界面漂亮,那么自定义界面就必不可少.其中有一个操作就是是我们每一个Qter开发者都要会的,而且是经常进行的. Qt::FramelessWindowHint这个属性想必大家都使用过,有些同学可能对这个属性很了解,也用的是炉火纯青,今天我们也来说说这个属性. 关于这个无边框属性网上也有一些文章,有些谈论的是b

EasyUI, Dialog 在框架页(ifrmae)的Top页面弹出时,拖拽Dialog边缘(以改变窗口大小),UI界面被卡死的解决办法

将Dialog的modal属性设置为true,可以解决卡死的问题(但会给用户使用体验带来影响) 1 var par = { 2 title: This.title, 3 width: This.width, 4 height: This.height, 5 cache: This.cache, 6 modal: This.modal, 7 resizable: This.resizable, 8 maximizable: This.maximizable, 9 onResize: This.on

css--画一个带有边框的三角形(类似于QQ的聊天框)

使用css画出如下的聊天框: 结构:<div class="chat"></div> css: .chat{ width: 300px; height: 80px; border: 1px solid #ccc; position: relative; } .chat:before{ content: ""; position: absolute; left: -10px; top: 34px; border-top: 6px solid t

Android自定义控件:类QQ未读消息拖拽效果

QQ的未读消息,算是一个比较好玩的效果,趁着最近时间比较多,参考了网上的一些资料之后,本次实现一个仿照QQ未读消息的拖拽小红点,最终完成效果如下: 首先我们从最基本的原理开始分析,看一张图: 这个图该怎么绘制呢?实际上我们这里是先绘制两个圆,然后将两个圆的切点通过贝塞尔曲线连接起来就达到这个效果了.至于贝塞尔曲线的概念,这里就不多做解释了,百度一下就知道了. 切点怎么算呢,这里我们稍微复习一些初中的数学知识.看了这个图之后,求出四个切点应该是轻而易举了. 现在思路已经很清晰了,按照我们的思路,开

Android drawPath实现QQ拖拽泡泡

这两天学习了使用Path绘制贝塞尔曲线相关,然后自己动手做了一个类似QQ未读消息可拖拽的小气泡,效果图如下: 接下来一步一步的实现整个过程. 基本原理 其实就是使用Path绘制三点的二次方贝塞尔曲线来完成那个妖娆的曲线的.然后根据触摸点不断绘制对应的圆形,根据距离的改变改变原始固定圆形的半径大小.最后就是松手后返回或者爆裂的实现. Path介绍: 顾名思义,就是一个路径的意思,Path里面有很多的方法,本次设计主要用到的相关方法有 moveTo() 移动Path到一个指定的点 quadTo()

实现一个简单的视频聊天室(源码)

在 <实现一个简单的语音聊天室>一文发布后,很多朋友建议我也实现一个视频聊天室给他们参考一下,其实,视频聊天室与语音聊天室的原理是差不多的,由于加入了摄像头.视频的处理,逻辑会繁杂一些,本文就实现一个简单的多人视频聊天系统,让多个人可以进入同一个房间进行语音视频沟通.先看看3个人进行视频聊天的运行效果截图:       上面两张截图分别是:登录界面.标注了各个控件的视频聊天室的主界面. 一. C/S结构 很明显,我这个语音聊天室采用的是C/S结构,整个项目结构相对比较简单,如下所示: 同语音聊

C#实现多人语音聊天

在上一篇文章 实现一个简单的语音聊天室(多人语音聊天系统)中,我用C#实现了一个简单的语音聊天室,并给出了源码下载.虽然有源码,但是很多朋友反映,理解起来还是有些模糊.不够清楚.现在想来,是因为我忘了先将底层的原理介绍一下,语音聊天室是基于OMCS实现的,那么这里我就补上OMCS中与多人语音视频相关部分的原理及方案的介绍. 一. 动态组 OMCS采用"动态组"的模式来实现多人语音/视频聊天组,所谓"动态组",就是在运行时动态创建和销毁的组,其包含如下几层意思: (1