服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - IOS - IOS设置QQ小红点消除的方法(一键退朝)

IOS设置QQ小红点消除的方法(一键退朝)

2021-01-06 14:47阿曌 IOS

这篇文章主要介绍了IOS设置QQ小红点消除的方法(一键退朝),对ios设置小红点消除相关知识感兴趣的朋友一起学习吧

qq上黏黏的小红点很好玩有木有,于是自己也想实现一番,看到ios实现的人比较少,android的比较多,于是这个就用ios来实现哈~

效果图:

IOS设置QQ小红点消除的方法(一键退朝)

调试图:

IOS设置QQ小红点消除的方法(一键退朝)

其实从实现来讲,我是先实现第二张图的效果的。

服务器之家小编给大家说下实现思路

1.了解原理,以及如何绘制“黏黏”形状(即绘制两圆加两条贝塞尔曲线)。

2.新建uiview(azmetaballcanvas),作为单独画布用来绘制“黏黏”形状,用程序实现算法,并绘制出来。

3.给画布(azmetaballcanvas)添加attach:(uiview *)方法,并添加手势监听,重绘,使得任意 view 都能够被在画布上拥有“黏黏”效果。

4.根据连心线的距离加上判断是否要断开,用户手指离开时也要根据距离来判断是爆炸动画还是回弹动画。

详细过程

首先必须要了解小红点拖拽的过程形状是什么,其实就是类似元球效果(metaball)。仔细观察可分析发现,就是两个大小不一样的圆加上两条贝塞尔曲线构成的。

IOS设置QQ小红点消除的方法(一键退朝)

关于算法部分,我已经分解成了另外一篇博文,强烈建议不清楚该形状是怎么画出来的同学先看一下《【算法分析】qq“一键退朝”之详细计算方法》

1.绘制拖拽

既然怎么求坐标点画出来我们已经知道了,现在就可以去实现了。

首先新建一个“画布”,继承自uiview

?
1
2
3
4
5
//azmetaballcanvas.h
@interface azmetaballcanvas : uiview
@property(nonatomic,strong) circle *centercircle;
@property(nonatomic,strong) circle *touchcircle;
@end

circle为自定义实体类,里面定义了一些圆的基本属性,如圆心坐标、半径等。

为什么要新建一个画布?

因为小红点是能够全屏拖动的,别看qq上它存在某一行cell,但其实你可以把它拉到别的cell上去,这就需要给小红点足够的位置来绘制,就干脆新建一个画布专门用来绘制小红点的动作好了。

azmetaballcanvas目前包含两个属性,两个圆,一个中心圆,一个触摸圆,按照需求来看,中心圆应该是位置不变的,触摸圆会跟随手指触摸屏幕的位置而改变,后面需要在两个圆之间画上贝塞尔曲线来构成元球效果。

接下来开始写azmetaballcanvas的实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//azmetaballcanvas.m
#define radius 40.0
@interface azmetaballcanvas() {
uibezierpath *_path;
cgpoint _touchpoint;
}
@end
@implementation azmetaballcanvas
- (instancetype)initwithcoder:(nscoder *)adecoder {
self = [super initwithcoder:adecoder];
nslog(@"initwithcorder");
if (self) {
[self initdata];
}
return self;
}
- (void)initdata {
_touchcircle = [circle initwithcenterpoint:self.center radius:radius];
_centercircle = [circle initwithcenterpoint:self.center radius:radius];
_touchpoint = self.center;
nslog(@"self.center (%f, %f)", self.center.x, self.center.y);
}
@end

先初始化两个圆的位置,默认在view的中心,并在init、initwithframe、initwithcoder等父类构造函数中加入自定义初始化方法initdata。

重写绘制方法

如同android中的ondraw(),ios中的drawrect能够被重写绘制,然后调用[view setneedsdisplay]来通知重绘。

?
1
2
3
4
5
6
7
8
9
- (void)drawrect:(cgrect)rect {
_path = [[uibezierpath alloc] init];
 
[self drawcentercircle];
 
[self drawtouchcircle:_touchpoint];
 
[self drawbeziercurvewithcircle1:_centercircle circle2:_touchcircle];
}

如同算法分析中所讲,在绘制的时候,我们只需要绘制两个圆(drawcentercircle、drawtouchcircle)和连接两圆的贝塞尔曲线(drawbeziercurve),算法其实就是照抄《【算法分析】qq“一键退朝”之详细计算方法》

ios自带贝塞尔曲线uibezierpath,其自带画圆方法addarcwithcenter: radius: startangle: endangle: clockwise:,所以我们只要调用就好啦!

?
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
#pragma mark draw circle --- 画圆
- (void) drawcentercircle {
[self drawcircle:_path circle:_centercircle];
}
- (void) drawtouchcircle:(cgpoint)center {
_touchcircle.centerpoint = center;
[self drawcircle:_path circle:_touchcircle];
}
- (void)drawcircle:(uibezierpath *)path circle:(circle *)circle {
[_path addarcwithcenter:circle.centerpoint radius:circle.radius startangle:0 endangle:360 clockwise:true];
[_path fill];
[_path stroke];
[_path removeallpoints];
}
 
#pragma mark draw curve --- 画贝塞尔曲线
- (void)drawbeziercurvewithcircle1:(circle *)circle1 circle2:(circle *)circle2 {
float circle1_x = circle1.centerpoint.x;
float circle1_y = circle1.centerpoint.y;
float circle2_x = circle2.centerpoint.x;
float circle2_y = circle2.centerpoint.y;
//连心线的长度
float d = sqrt(powf(circle1_x - circle2_x, 2) + powf(circle1_y - circle2_y, 2));
//连心线x轴的夹角
float angle1 = atan((circle2_y - circle1_y) / (circle1_x - circle2_x));
//连心线和公切线的夹角
float angle2 = asin((circle1.radius - circle2.radius) / d);
//切点到圆心和x轴的夹角
float angle3 = m_pi_2 - angle1 - angle2;
float angle4 = m_pi_2 - angle1 + angle2;
float offset1_x = cos(angle3) * circle1.radius;
float offset1_y = sin(angle3) * circle1.radius;
float offset2_x = cos(angle3) * circle2.radius;
float offset2_y = sin(angle3) * circle2.radius;
float offset3_x = cos(angle4) * circle1.radius;
float offset3_y = sin(angle4) * circle1.radius;
float offset4_x = cos(angle4) * circle2.radius;
float offset4_y = sin(angle4) * circle2.radius;
float p1_x = circle1_x - offset1_x;
float p1_y = circle1_y - offset1_y;
float p2_x = circle2_x - offset2_x;
float p2_y = circle2_y - offset2_y;
float p3_x = circle1_x + offset3_x;
float p3_y = circle1_y + offset3_y;
float p4_x = circle2_x + offset4_x;
float p4_y = circle2_y + offset4_y;
cgpoint p1 = cgpointmake(p1_x, p1_y);
cgpoint p2 = cgpointmake(p2_x, p2_y);
cgpoint p3 = cgpointmake(p3_x, p3_y);
cgpoint p4 = cgpointmake(p4_x, p4_y);
cgpoint p1_center_p4 = cgpointmake((p1_x + p4_x) / 2, (p1_y + p4_y) / 2);
cgpoint p2_center_p3 = cgpointmake((p2_x + p3_x) / 2, (p2_y + p3_y) / 2);
[self drawbeziercurvestartat:p1 endat:p2 controlpoint:p2_center_p3];
[self drawlinestartat:p2 endat:p4];
[self drawbeziercurvestartat:p4 endat:p3 controlpoint:p1_center_p4];
[self drawlinestartat:p3 endat:p1];
[_path movetopoint:p1];
[_path closepath];
[_path stroke];
}

2.监听手势

简单版

最简单的其实就是直接在azmetaballcanvas中重写touchxxx等一系列方法,然后在其中调用setneedsdisplay通知uiview重绘。

?
1
2
3
4
5
6
7
8
9
10
11
#pragma mark touch event
- (void)touchesbegan:(nsset<uitouch *> *)touches withevent:(uievent *)event {
uitouch *touch = [touches anyobject];
_touchpoint = [touch locationinview:self];
[self setneedsdisplay];
}
- (void)touchesmoved:(nsset<uitouch *> *)touches withevent:(uievent *)event {
uitouch *touch = [touches anyobject];
_touchpoint = [touch locationinview:self];
[self setneedsdisplay];
}

现在其实差不多第二张图的效果已经出来了,差的就是更改两圆的半径方法。

IOS设置QQ小红点消除的方法(一键退朝)

改变半径的方法就非常简单了

?
1
2
3
4
5
6
7
8
9
#pragma 改变半径
-(void)changecentercircleradiusto:(float)radius {
_centercircle.radius = radius;
[self setneedsdisplay];
}
-(void)changetouchcircleradiusto:(float)radius {
_touchcircle.radius = radius;
[self setneedsdisplay];
}

普通版

根据现象发现,我们需要通过拖拽小红点来移动它,而不是我们手指点哪,小红点就在哪,所以我们需要给小红点增加手势监听,而不是“画布”。

于是我们改为在画布添加方法- (void)attach:(uiview *)item;,然后再给传入的view添加pan手势。

?
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
- (void)attach:(uiview *)item {
uipangesturerecognizer *drag = [[uipangesturerecognizer alloc] initwithtarget:self action:@selector(drag:)];
item.userinteractionenabled = yes;
[item addgesturerecognizer:drag];
}
- (void)drag:(uipangesturerecognizer *)recognizer {
//得到触摸点
_touchpoint = [recognizer locationinview:self];
//得到触摸的view
uiview *touchview = recognizer.view;
switch (recognizer.state) {
case uigesturerecognizerstatebegan:{
//touch开始:在画布上绘制一个touchview的副本
//...此部分参看源码
break;
}
case uigesturerecognizerstatechanged:{
//移动中:记录触摸位置,更改touchview和touchcircle的坐标位置
[self resettouchcenter:_touchpoint];
break;
}
case uigesturerecognizerstateended: {
//touch结束:根据连心线长度判断是执行爆炸动画还是弹簧动画
//...此部分参看源码
break;
}
default:
break;
}
[self setneedsdisplay]; //重绘
}

以上内容是小编给大家介绍的ios设置qq小红点消除的方法(一键退朝),希望对大家有所帮助。

延伸 · 阅读

精彩推荐
  • IOSiOS中tableview 两级cell的展开与收回的示例代码

    iOS中tableview 两级cell的展开与收回的示例代码

    本篇文章主要介绍了iOS中tableview 两级cell的展开与收回的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    J_Kang3862021-04-22
  • IOS解析iOS开发中的FirstResponder第一响应对象

    解析iOS开发中的FirstResponder第一响应对象

    这篇文章主要介绍了解析iOS开发中的FirstResponder第一响应对象,包括View的FirstResponder的释放问题,需要的朋友可以参考下...

    一片枫叶4662020-12-25
  • IOSiOS布局渲染之UIView方法的调用时机详解

    iOS布局渲染之UIView方法的调用时机详解

    在你刚开始开发 iOS 应用时,最难避免或者是调试的就是和布局相关的问题,下面这篇文章主要给大家介绍了关于iOS布局渲染之UIView方法调用时机的相关资料...

    windtersharp7642021-05-04
  • IOSiOS通过逆向理解Block的内存模型

    iOS通过逆向理解Block的内存模型

    自从对 iOS 的逆向初窥门径后,我也经常通过它来分析一些比较大的应用,参考一下这些应用中某些功能的实现。这个探索的过程乐趣多多,不仅能满足自...

    Swiftyper12832021-03-03
  • IOSiOS 雷达效果实例详解

    iOS 雷达效果实例详解

    这篇文章主要介绍了iOS 雷达效果实例详解的相关资料,需要的朋友可以参考下...

    SimpleWorld11022021-01-28
  • IOSIOS开发之字典转字符串的实例详解

    IOS开发之字典转字符串的实例详解

    这篇文章主要介绍了IOS开发之字典转字符串的实例详解的相关资料,希望通过本文能帮助到大家,让大家掌握这样的方法,需要的朋友可以参考下...

    苦练内功5832021-04-01
  • IOS关于iOS自适应cell行高的那些事儿

    关于iOS自适应cell行高的那些事儿

    这篇文章主要给大家介绍了关于iOS自适应cell行高的那些事儿,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的...

    daisy6092021-05-17
  • IOSIOS 屏幕适配方案实现缩放window的示例代码

    IOS 屏幕适配方案实现缩放window的示例代码

    这篇文章主要介绍了IOS 屏幕适配方案实现缩放window的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要...

    xiari5772021-06-01