QQ粘性布局


整體思路:

手指移動,按鈕跟著移動.按鈕跟著手指移動.移動時底部有一個圓,

根據上面的大圓按鈕拖動的距離,小圓的半徑在變小.移動時中間有一塊不規則的填充區域.

手指移動超出一定的范圍,填充效果消失,當手指松開時.判斷當前大圓距離與小圓之間的距離.

如果小于60就讓大圓回來原來的位置.下次拖動時,同樣具有填充效果.

如果大于60,手指松開時,播放一個動畫.動畫完成時, 刪除動畫按鈕.

實現步驟:

1.自定義大圓控件(UIButton)可以顯示背景圖片,和文字

按鈕定義的時候要在初始方法中,把它的基本屬性設置好.在開始加載的時候設置.

基本屬性包括顏色,圓角,文字顏色,大小.

實現代碼:

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.讓大圓控件隨著手指移動而移動

添加手勢.同樣也是在初始化方法當中進行設置.

注意不能根據形變修改大圓的位置,只能通過center,因為全程都需要用到中心點計算。

tansform并沒有修改center,它修改的是Frame.

添加手勢代碼為:

UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]?initWithTarget:self action:@selector(pan:)];

[self addGestureRecognizer:pan];

手勢實現方法為:

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];

當手指拖動大圓時,小圓的半徑會根據拖動的距離進行減小.所以要計算出兩個圓之間的距離.

計算完畢后.讓小圓的原始半徑每次都減去一個距離比例.重新設置尺寸大小.和小圓的半徑.

計算兩個圓之間距離是一個功能單獨抽出來.

方法為:

- (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;

}

在手指拖動方法計算兩個圓之間的距離, 根據拖動的距離讓小圓的半徑增大減小.

實現代碼為:

CGFloat distance = [self distanceWithSmallCircle:self.smallCircle bigCircle:self];

取出小圓的半徑

注意這里是取出小圓最初的寬度,由于每次拖動的時候都會去修改小圓的寬高.所以這里不能直接用小圓的寬度

這里用的是大圓的寬度,開始小圓和大圓的寬度是一樣的.

大圓在移動時,大圓的寬高沒有發現變化,所以可以拿到大圓的寬高

CGFloat smallR = self.bounds.size.width * 0.5;

讓小圓的半徑每次減去一個距離比例

smallR = smallR - distance / 10.0;

每次移動時,重新設置小圓的寬高

self.smallCircle.bounds = CGRectMake(0, 0, smallR * 2, smallR * 2);

重新設置小圓的圓角

self.smallCircle.layer.cornerRadius = smallR;

4.添加粘性效果

中間的粘性效果其實就是一塊填充區域.只要把這個填充區域的路徑給求出來就行了.

中間的路徑通過確定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θ);

創建路徑,把這些點連接到一起

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];

以上是根據兩個圓求出不規則的矩形

求出路徑后,要把路徑填充起來.但是不能夠直接給填充到當前的按鈕之上.按鈕是可以拖動的.

繪制東西,當超出它的范圍以外就不會再繪制.

所以要把路徑添加到按鈕的父控件當中, 但是當前是一個路徑,是不能夠直接添加到父控件當中的.

可能過形狀圖層添加.

形狀圖層會根據一個路徑生成一個形狀.把這個形狀添加到當前控件的圖片父層就可以了.

添加時需要注意:

形狀圖層之有一個,所以不能夠在手指拖動方法當中添加.由于當手指拖動的距離超過某個范圍后,形狀圖片會被移除.

下一次再去移動時, 還會有填充的路徑.所以把創建形狀圖層搞成一個懶加載的形式,

如果發現下一次被刪除時,再重新創建.

形式為:

-(CAShapeLayer *)shap{

if (_shap == nil) {

創建形狀圖層

CAShapeLayer *shap = [CAShapeLayer layer];

設置形狀圖層的填充顏色

shap.fillColor = [UIColor redColor].CGColor;

self.shap = shap;

把形狀圖層添加到當前按鈕的父層當中.

[self.superview.layer insertSublayer:shap atIndex:0];

_shap = shap;

}

return _shap;

}

在手指移動方法當中,給形狀圖層賦值路徑就可以了.

5.粘性業務邏輯處理

在手指移動方法判斷兩個圓之間的距離, 如果發現兩個圓之間的距離超過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.手指停止拖動業務邏輯

移動后手指松開時判斷兩個圓之間的距離,如果兩個圓之間的距離小于60時,讓大圓復位.小圓顯示.

手指松開時,如果兩個圓之間的距離大于60時.播放一個動畫.動畫播放完畢時.把當前按鈕從父控件當中移除.

播放一個動畫.

創建一個UIImageView,尺寸和當前按鈕一樣大.

UIImageView *imageV = [[UIImageView alloc] initWithFrame:self.bounds];

創建動畫圖片

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];

}

設置動畫圖片數組

imageV.animationImages = imageArray;

設置動畫執行時長

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轉自動布局

不然會出現按鈕回原位的情況.

self.view.translatesAutoresizingMaskIntoConstraints = NO;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容