原文出处: Booooooooom   

闲来无事,分享一个最近在某个地方借鉴的一个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,实现了小球的移动。

Objective-C
1
2
3

//将触摸点赋值给touchView的中心点 也就是根据触摸的位置实时修改view的位置
    self.center = [startTouch locationInView:self.superview];

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

Objective-C
1
2
3

// 移除之前的所有行为
    // 如果不移除,之前移动所触发的物理吸附事件就会一直执行
    [self.animator removeAllBehaviors];

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

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

这是.h里面的代码

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

Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13

#import
typedef void (^DownLoadBlock) ();
@interface xuanfuwu : UIView
@property (nonatomic ,assign) CGPoint startPoint;//触摸起始点
@property (nonatomic ,assign) CGPoint endPoint;//触摸结束点
@property (nonatomic ,copy) DownLoadBlock downLoadBlock;
@end

这是.m里面的代码

Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295

//主题颜色
#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];
    }];
}

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

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

    语音聊天室,或多人语音聊天,是即时通信应用中常见的功能之一,比如,QQ的语音讨论组就是我们用得比较多的. 这篇文章将实现一个简单的语音聊天室,让多个人可以进入同一个房间进行语音沟通.先看运行效果截图: ...

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

    多人语音聊天,或语音聊天室,是即时通信应用中常见的功能之一,比如,QQ的语音讨论组就是我们用得比较多的. 本文将基于最新版本的OMCS(V3.5)实现一个简单的语音聊天室,让多个人可以进入同一个房间进 ...

  3. 【WPF】一个类似于QQ面板的GroupShelf控件

    最近做控件上了瘾,现在把做的一个类似于QQ面板的控件放上来. [分析] 从整体来看,这个控件应该同ListBox,ListView这类控件一样,是一个ItemsControl,而中间的项,就是它的It ...

  4. java qq聊天界面_【附源码】用Java写了一个类QQ界面聊天小项目,可在线聊天!...

    原标题:[附源码]用Java写了一个类QQ界面聊天小项目,可在线聊天! 目录: 1.功能实现 2.模块划分 3.使用到知识 4.部分代码实现 5.运行例图 1.功能实现 1.修改功能(密码.昵称.个性 ...

  5. android气泡聊天消息背景,Android使用贝塞尔曲线仿QQ聊天消息气泡拖拽效果

    本文实例为大家分享了Android仿QQ聊天消息气泡拖拽效果展示的具体代码,供大家参考,具体内容如下 先画圆,都会吧.代码如下: public class Bezier extends View { ...

  6. Unity 基础 之 在 UGUI 上简单实现VideoPlayer视频播放的功能,简单暂停播放/显示视频名称/显示时长/拖拽播放等

    Unity 基础 之 在 UGUI 上简单实现VideoPlayer视频播放的功能,简单暂停播放/显示视频名称/显示时长/拖拽播放等 目录 Unity 基础 之 在 UGUI 上简单实现VideoPl ...

  7. Android自定义可拖拽的悬浮按钮---DragFloatingActionButton

    悬浮按钮FloatingActionButton是Android 5.0系统添加的新控件,FloatingActionButton是继承至ImageView,所以FloatingActionButto ...

  8. vue 悬浮图标_vue实现可拖拽移动悬浮球

    拖拽移动悬浮球 需求拆解 1.元素悬浮于全屏 2.元素可拖拽 3.元素拖拽结束后会吸附贴壁 4.元素单击唤出菜单 5.菜单展开时点击空白处关闭菜单 6.菜单不可拖拽 7.元素拖拽时菜单不打开 8.元素 ...

  9. Android进阶之路 - 可拖拽的悬浮按钮

    类似文章在CSDN上有很多,但是几经查找之后原文其实产于简书的一位作者: 综合几篇文章,在原有基础上我会尽可能全面总结一下 效果图 实现思路 通过重写控件的onTouchEvent方法监听触摸效果 通 ...

最新文章

  1. 在Andoird studio 中用代码实现setId报错,而在ecplise中可以,的处理方法
  2. 豪斯荷尔德变换及变形QR算法对矩阵进行奇异值分解VB算法
  3. php和python交互-Python如何实现简单的用户交互程序(示例)
  4. 【嵌入式】Libmodbus源码分析(二)-常用接口函数分析
  5. Jsoup代码解读之六-parser(下)
  6. Linux中vim编辑器的缩进的功能键
  7. 设计模式11---组合模式(Composite Pattern)
  8. css那些事儿4 背景图像
  9. 客座编辑:季统凯(1972-),男,博士,中国科学院云计算产业技术创新与育成中心研究员、主任。...
  10. 吴恩达神经网络和深度学习-学习笔记-39-计算机视觉现状
  11. NOIP 2005 等价表达式 (TYVJ P1060)
  12. OpenCv中 width 和 widthStep的区别
  13. 数据结构 图论02 十字链表详解 代码
  14. Arduino nRF52840高级蓝牙5
  15. Python编程 | 颜色分类
  16. 编译时的chenk api
  17. jsPlumb 学习笔记(1)(api部分翻译)
  18. SpringCloud版本Hoxton SR5 --- 第五讲:zuul 路由、过滤、容错与回退、集群、高可用
  19. Vue.js实战——内置指令(一)
  20. 计算机管理记事本,电脑记事本软件

热门文章

  1. Qt quick性能提升
  2. [[概率论与数理统计-2]:随机函数、概率、概率函数、概率分布函数
  3. python爬虫初战之小说爬取
  4. Ping ip的几种情况
  5. Netty、NIO、多线程
  6. Linux 面试题(史上最全面试题,精心整理100家互联网企业,面试必过)
  7. 5G将催动物联网成为新风口
  8. WAR3 天地劫技能代码节选004
  9. 有点甜用计算机怎么谈,太有才了,北大毕业生改编的《哲学有点甜》(用于必修4开篇第一课导入)...
  10. 如何访问嵌入式设备sd卡上的文件