整體思路:
手指移動,按鈕跟著移動.按鈕跟著手指移動.移動時底部有一個圓,
根據(jù)上面的大圓按鈕拖動的距離,小圓的半徑在變小.移動時中間有一塊不規(guī)則的填充區(qū)域.
手指移動超出一定的范圍,填充效果消失,當手指松開時.判斷當前大圓距離與小圓之間的距離.
如果小于60就讓大圓回來原來的位置.下次拖動時,同樣具有填充效果.
如果大于60,手指松開時,播放一個動畫.動畫完成時, 刪除動畫按鈕.
實現(xiàn)步驟:
1.自定義大圓控件(UIButton)可以顯示背景圖片携御,和文字
按鈕定義的時候要在初始方法中,把它的基本屬性設(shè)置好.在開始加載的時候設(shè)置.
基本屬性包括顏色,圓角,文字顏色,大小.
實現(xiàn)代碼:
self.backgroundColor = [UIColor redColor];
self.layer.cornerRadius = self.bounds.size.width * 0.5;
self.titleLabel.font = [UIFont systemFontOfSize:12];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
2.讓大圓控件隨著手指移動而移動
添加手勢.同樣也是在初始化方法當中進行設(shè)置.
注意不能根據(jù)形變修改大圓的位置,只能通過center,因為全程都需要用到中心點計算际邻。
tansform并沒有修改center,它修改的是Frame.
添加手勢代碼為:
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]?initWithTarget:self action:@selector(pan:)];
[self addGestureRecognizer:pan];
手勢實現(xiàn)方法為:
CGPoint transP = [pan translationInView:self];
CGPoint center = self.center;
center.x += transP.x;
center.y +=transP.y;
self.center = center;
注意要做復位,相對于上一次.
[pan setTranslation:CGPointZero inView:self];
3.在拖動的時候氯哮,添加一個小圓控件在原來大圓控件的位置
在初始化方法中添加小圓
注意:添加小圓時不能夠直接添加在當前按鈕上,因為按鈕是可以移動的,如果直接添加在按鈕,它會跟著按鈕一起移動.
所以以把小圓添加到按鈕父控件當中.添加時注意,要把小圓添加到按鈕底部.不然會把按鈕給蓋起來.
UIView *smallCircle = [[UIView alloc] init];
smallCircle.frame = self.frame;
smallCircle.layer.cornerRadius = self.layer.cornerRadius;
smallCircle.backgroundColor = self.backgroundColor;
self.smallCircle = smallCircle;
[self.superview insertSubview:smallCircle belowSubview:self];
當手指拖動大圓時,小圓的半徑會根據(jù)拖動的距離進行減小.所以要計算出兩個圓之間的距離.
計算完畢后.讓小圓的原始半徑每次都減去一個距離比例.重新設(shè)置尺寸大小.和小圓的半徑.
計算兩個圓之間距離是一個功能單獨抽出來.
方法為:
- (CGFloat)distanceWithSmallCircle:(UIView *)smallView bigCircle:(UIView *)bigCircle{
X軸的便宜量
CGFloat offsetX = bigCircle.center.x - smallView.center.x;
Y軸的便宜量
CGFloat offsetY = bigCircle.center.y - smallView.center.y;
CGFloat distance = sqrt(offsetX * offsetX + offsetY * offsetY);
return distance;
}
在手指拖動方法計算兩個圓之間的距離, 根據(jù)拖動的距離讓小圓的半徑增大減小.
實現(xiàn)代碼為:
CGFloat distance = [self distanceWithSmallCircle:self.smallCircle bigCircle:self];
取出小圓的半徑
注意這里是取出小圓最初的寬度,由于每次拖動的時候都會去修改小圓的寬高.所以這里不能直接用小圓的寬度
這里用的是大圓的寬度,開始小圓和大圓的寬度是一樣的.
大圓在移動時,大圓的寬高沒有發(fā)現(xiàn)變化,所以可以拿到大圓的寬高
CGFloat smallR = self.bounds.size.width * 0.5;
讓小圓的半徑每次減去一個距離比例
smallR = smallR - distance / 10.0;
每次移動時,重新設(shè)置小圓的寬高
self.smallCircle.bounds = CGRectMake(0, 0, smallR * 2, smallR * 2);
重新設(shè)置小圓的圓角
self.smallCircle.layer.cornerRadius = smallR;
4.添加粘性效果
中間的粘性效果其實就是一塊填充區(qū)域.只要把這個填充區(qū)域的路徑給求出來就行了.
中間的路徑通過確定6個點.把這些點連接出來就行.
求點為:
x1,y1分別是小圓的圓心
x2,y2分別是大圓的圓心
r1代表小圓的半徑
r2代表大圓的半徑
d是兩個圓之間的距離
y軸的偏移量 / 兩個圓之間的距離
cosθ = (y2 - y1) / d;
x軸的偏移量 / 兩個圓之間的距離
sinθ = (x2 - x1) / d;
已知一個角,一個斜邊
角的鄰邊 = 斜邊 * cosθ
角的對邊 = 斜邊 * sinθ
CGPoint pointA = CGPointMake(x1 - r1 * cosθ, y1 + r1 * sinθ);
CGPoint pointB = CGPointMake(x1 + r1 * cosθ, y1 - r1 * sinθ);
CGPoint pointC = CGPointMake(x2 + r2 * cosθ, y2 - r2 * sinθ);
CGPoint pointD = CGPointMake(x2 - r2 * cosθ, y2 + r2 * sinθ);
CGPoint pointO = CGPointMake(pointA.x + d * 0.5 * sinθ, pointA.y + d * 0.5 * cosθ);
CGPoint pointP = CGPointMake(pointB.x + d * 0.5 * sinθ, pointB.y + d * 0.5 * cosθ);
創(chuàng)建路徑,把這些點連接到一起
UIBezierPath *path = [UIBezierPath bezierPath];
AB
[path moveToPoint:pointA];
[path addLineToPoint:pointB];
BC(曲線)
[path addQuadCurveToPoint:pointC controlPoint:pointP];
CD
[path addLineToPoint:pointD];
DA(曲線)
[path addQuadCurveToPoint:pointA controlPoint:pointO];
以上是根據(jù)兩個圓求出不規(guī)則的矩形
求出路徑后,要把路徑填充起來.但是不能夠直接給填充到當前的按鈕之上.按鈕是可以拖動的.
繪制東西,當超出它的范圍以外就不會再繪制.
所以要把路徑添加到按鈕的父控件當中, 但是當前是一個路徑,是不能夠直接添加到父控件當中的.
可能過形狀圖層添加.
形狀圖層會根據(jù)一個路徑生成一個形狀.把這個形狀添加到當前控件的圖片父層就可以了.
添加時需要注意:
形狀圖層之有一個,所以不能夠在手指拖動方法當中添加.由于當手指拖動的距離超過某個范圍后,形狀圖片會被移除.
下一次再去移動時, 還會有填充的路徑.所以把創(chuàng)建形狀圖層搞成一個懶加載的形式,
如果發(fā)現(xiàn)下一次被刪除時,再重新創(chuàng)建.
形式為:
-(CAShapeLayer *)shap{
if (_shap == nil) {
創(chuàng)建形狀圖層
CAShapeLayer *shap = [CAShapeLayer layer];
設(shè)置形狀圖層的填充顏色
shap.fillColor = [UIColor redColor].CGColor;
self.shap = shap;
把形狀圖層添加到當前按鈕的父層當中.
[self.superview.layer insertSublayer:shap atIndex:0];
_shap = shap;
}
return _shap;
}
在手指移動方法當中,給形狀圖層賦值路徑就可以了.
5.粘性業(yè)務(wù)邏輯處理
在手指移動方法判斷兩個圓之間的距離, 如果發(fā)現(xiàn)兩個圓之間的距離超過60時
讓底部的小圓隱藏.把路徑移除
當小圓顯示的時候才繪制填充路徑
if (self.smallCircle.hidden == NO) {
UIBezierPath *path = [self pathWithSmallCircle:self.smallCircle bigCircle:self];
self.shap.path = path.CGPath;
}
當兩個圓之間的距離超過60時.
if(distance > 60){
移除填充路徑
[self.shap removeFromSuperlayer];
讓底部的小圓隱匿
self.smallCircle.hidden = YES;
}
6.手指停止拖動業(yè)務(wù)邏輯
移動后手指松開時判斷兩個圓之間的距離,如果兩個圓之間的距離小于60時,讓大圓復位.小圓顯示.
手指松開時,如果兩個圓之間的距離大于60時.播放一個動畫.動畫播放完畢時.把當前按鈕從父控件當中移除.
播放一個動畫.
創(chuàng)建一個UIImageView,尺寸和當前按鈕一樣大.
UIImageView *imageV = [[UIImageView alloc] initWithFrame:self.bounds];
創(chuàng)建動畫圖片
NSMutableArray *imageArray = [NSMutableArray array];
for (int i = 0; i < 8 ; i++) {
NSString *imageName = [NSString stringWithFormat:@"%d",i+1];
UIImage *image = [UIImage imageNamed:imageName];
[imageArray addObject:image];
}
設(shè)置動畫圖片數(shù)組
imageV.animationImages = imageArray;
設(shè)置動畫執(zhí)行時長
imageV.animationDuration = 1;
開始動畫
[imageV startAnimating];
把UIImageView添加到當前按鈕上
[self addSubview:imageV];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[self removeFromSuperview];
});
注意:
在控制器加載完畢后,要取消Autoresizing轉(zhuǎn)自動布局
不然會出現(xiàn)按鈕回原位的情況.
self.view.translatesAutoresizingMaskIntoConstraints = NO;