闲来无事,分享一个最近在某个地方借鉴的一个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,实现了小球的移动。
1
2
3
|
//将触摸点赋值给touchView的中心点 也就是根据触摸的位置实时修改view的位置
self
.
center
=
[
startTouch
locationInView
:self
.
superview
]
;
|
还有一个注意的是在开始移动之前一定要把之前的效果给移除掉,不然会很奇葩的,不信你也可以试一试。
1
2
3
|
// 移除之前的所有行为
// 如果不移除,之前移动所触发的物理吸附事件就会一直执行
[
self
.
animator
removeAllBehaviors
]
;
|
第四:就是计算计算距离最近的边缘 吸附到边缘停靠,具体的计算就不说了,因为太繁琐了,没有想着优化,看看啥时候闲下来再来看看能不能优化吧。主要就是考虑的情况稍微多了点,但是逻辑上还是很清晰的。这里有一个吸附的物理行为,其实也就是一个动画效果,直接加上就好了,也不是很复杂。
好的,具体的都解释完了。上代码!!!,突然发现并不能传文件,好吧,赋值过来看看吧
这是.h里面的代码
这里只是给外界留了个借口,这些属性也可以.m里面,我只是在外界用到了这个而已。
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里面的代码
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
]
;
}
]
;
}
|