IOS設(shè)置QQ小紅點消除的方法(一鍵退朝)
QQ上黏黏的小紅點很好玩有木有,于是自己也想實現(xiàn)一番,看到iOS實現(xiàn)的人比較少,Android的比較多,于是這個就用iOS來實現(xiàn)哈~
效果圖:
調(diào)試圖:
其實從實現(xiàn)來講,我是先實現(xiàn)第二張圖的效果的。
腳本之家小編給大家說下實現(xiàn)思路
1.了解原理,以及如何繪制“黏黏”形狀(即繪制兩圓加兩條貝塞爾曲線)。
2.新建UIView(AZMetaBallCanvas),作為單獨畫布用來繪制“黏黏”形狀,用程序?qū)崿F(xiàn)算法,并繪制出來。
3.給畫布(AZMetaBallCanvas)添加attach:(UIView *)方法,并添加手勢監(jiān)聽,重繪,使得任意 view 都能夠被在畫布上擁有“黏黏”效果。
4.根據(jù)連心線的距離加上判斷是否要斷開,用戶手指離開時也要根據(jù)距離來判斷是爆炸動畫還是回彈動畫。
詳細(xì)過程
首先必須要了解小紅點拖拽的過程形狀是什么,其實就是類似元球效果(MetaBall)。仔細(xì)觀察可分析發(fā)現(xiàn),就是兩個大小不一樣的圓加上兩條貝塞爾曲線構(gòu)成的。
關(guān)于算法部分,我已經(jīng)分解成了另外一篇博文,強烈建議不清楚該形狀是怎么畫出來的同學(xué)先看一下《【算法分析】QQ“一鍵退朝”之詳細(xì)計算方法》
1.繪制拖拽
既然怎么求坐標(biāo)點畫出來我們已經(jīng)知道了,現(xiàn)在就可以去實現(xiàn)了。
首先新建一個“畫布”,繼承自UIView
//AZMetaBallCanvas.h @interface AZMetaBallCanvas : UIView @property(nonatomic,strong) Circle *centerCircle; @property(nonatomic,strong) Circle *touchCircle; @end
Circle為自定義實體類,里面定義了一些圓的基本屬性,如圓心坐標(biāo)、半徑等。
為什么要新建一個畫布?
因為小紅點是能夠全屏拖動的,別看QQ上它存在某一行Cell,但其實你可以把它拉到別的Cell上去,這就需要給小紅點足夠的位置來繪制,就干脆新建一個畫布專門用來繪制小紅點的動作好了。
AZMetaBallCanvas目前包含兩個屬性,兩個圓,一個中心圓,一個觸摸圓,按照需求來看,中心圓應(yīng)該是位置不變的,觸摸圓會跟隨手指觸摸屏幕的位置而改變,后面需要在兩個圓之間畫上貝塞爾曲線來構(gòu)成元球效果。
接下來開始寫AZMetaBallCanvas的實現(xiàn)
//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
先初始化兩個圓的位置,默認(rèn)在View的中心,并在init、initWithFrame、initWithCoder等父類構(gòu)造函數(shù)中加入自定義初始化方法initData。
重寫繪制方法
如同Android中的onDraw(),iOS中的drawRect能夠被重寫繪制,然后調(diào)用[view setNeedsDisplay]來通知重繪。
- (void)drawRect:(CGRect)rect { _path = [[UIBezierPath alloc] init]; [self drawCenterCircle]; [self drawTouchCircle:_touchPoint]; [self drawBezierCurveWithCircle1:_centerCircle Circle2:_touchCircle]; }
如同算法分析中所講,在繪制的時候,我們只需要繪制兩個圓(drawCenterCircle、drawTouchCircle)和連接兩圓的貝塞爾曲線(drawBezierCurve),算法其實就是照抄《【算法分析】QQ“一鍵退朝”之詳細(xì)計算方法》
iOS自帶貝塞爾曲線UIBezierPath,其自帶畫圓方法addArcWithCenter: radius: startAngle: endAngle: clockwise:,所以我們只要調(diào)用就好啦!
#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.監(jiān)聽手勢
簡單版
最簡單的其實就是直接在AZMetaBallCanvas中重寫touchXXX等一系列方法,然后在其中調(diào)用setNeedsDisplay通知UIView重繪。
#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]; }
現(xiàn)在其實差不多第二張圖的效果已經(jīng)出來了,差的就是更改兩圓的半徑方法。
改變半徑的方法就非常簡單了
#pragma 改變半徑 -(void)changeCenterCircleRadiusTo:(float)radius { _centerCircle.radius = radius; [self setNeedsDisplay]; } -(void)changeTouchCircleRadiusTo:(float)radius { _touchCircle.radius = radius; [self setNeedsDisplay]; }
普通版
根據(jù)現(xiàn)象發(fā)現(xiàn),我們需要通過拖拽小紅點來移動它,而不是我們手指點哪,小紅點就在哪,所以我們需要給小紅點增加手勢監(jiān)聽,而不是“畫布”。
于是我們改為在畫布添加方法- (void)attach:(UIView *)item;,然后再給傳入的view添加Pan手勢。
- (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的坐標(biāo)位置 [self resetTouchCenter:_touchPoint]; break; } case UIGestureRecognizerStateEnded: { //touch結(jié)束:根據(jù)連心線長度判斷是執(zhí)行爆炸動畫還是彈簧動畫 //...此部分參看源碼 break; } default: break; } [self setNeedsDisplay]; //重繪 }
以上內(nèi)容是小編給大家介紹的IOS設(shè)置QQ小紅點消除的方法(一鍵退朝),希望對大家有所幫助。
相關(guān)文章
android中UIColletionView瀑布流布局實現(xiàn)思路以及封裝的實現(xiàn)
本篇文章主要介紹了android中UIColletionView瀑布流布局實現(xiàn)思路以及封裝的實現(xiàn),具有一定的參考價值,有興趣的可以了解一下。2017-02-02如何實現(xiàn)IOS_SearchBar搜索欄及關(guān)鍵字高亮
本文通過實例代碼演示如何實現(xiàn)IOS搜索欄及搜索關(guān)鍵字高亮,效果很棒,小編覺得對大家的學(xué)習(xí)會很有幫助,現(xiàn)在分享給大家,有需要的可以參考學(xué)習(xí)。2016-08-082016 cocoapods的安裝和使用方法以及版本升級遇到的問題
CocoaPods是一個負(fù)責(zé)管理iOS項目中第三方開源庫的工具,通過CocoaPods,我們可以將第三方的依賴庫統(tǒng)一管理起來,配置和更新只需要通過簡單的幾行命令即可完成,需要的朋友可以參考下2016-09-09IOS 使用Block二次封裝AFNetworking 3.0詳解
這篇文章主要介紹了IOS 使用Block二次封裝AFNetworking 3.0詳解的相關(guān)資料,需要的朋友可以參考下2017-02-02iOS實現(xiàn)點擊狀態(tài)欄自動回到頂部效果詳解
在IOS開發(fā)過程中,經(jīng)常會有這種需求,需要通過點擊狀態(tài)欄返回到頂部,給用戶更好的體驗效果,下面這篇文章給大家詳細(xì)介紹了實現(xiàn)過程,有需要的可以參考借鑒。2016-09-09