iOS-QQ黏性佈局

一.storyboard裏面如下:


二.自定義BageValueBtn

//  BageValueBtn.h

#import <UIKit/UIKit.h>

@interface BageValueBtn : UIButton

@end
//  BageValueBtn.m

#import "BageValueBtn.h"

@interface BageValueBtn()

/** <#註釋#> */
@property (nonatomic, weak) UIView *smallCircle;

/** <#註釋#> */
@property (nonatomic, weak) CAShapeLayer *shapL;


@end


@implementation BageValueBtn


-(CAShapeLayer *)shapL {
    
    if (_shapL == nil) {
        
        CAShapeLayer *shapL = [CAShapeLayer layer];
        [self.superview.layer insertSublayer:shapL atIndex:0];
        shapL.fillColor = [UIColor redColor].CGColor;
        _shapL = shapL;
    }
    return _shapL;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setUp];
    }
    return self;
}


-(void)awakeFromNib {
    
    [self setUp];
    
    //添加手勢
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [self addGestureRecognizer:pan];
    
    

    
    
}

- (void)pan:(UIPanGestureRecognizer *)pan {
    
    //拖動
    CGPoint transP = [pan translationInView:self];
    
    //transform,transform並沒有修改center.它修改的是frame
    
    //self.transform = CGAffineTransformTranslate(self.transform, transP.x, transP.y);
    
    CGPoint center = self.center;
    center.x += transP.x;
    center.y += transP.y;
    self.center = center;
    
    
    //NSLog(@"%@",NSStringFromCGPoint(self.center));
    
    //復位
    [pan setTranslation:CGPointZero inView:self];

    CGFloat distance = [self distanceWithSmallCircle:self.smallCircle BigCirCle:self];
    
    //讓小圓半徑根據距離的增大,半徑在減小
    CGFloat smallR = self.bounds.size.width * 0.5;
    smallR -= distance / 10.0;
    self.smallCircle.bounds = CGRectMake(0, 0, smallR * 2, smallR * 2);
    self.smallCircle.layer.cornerRadius = smallR;

    NSLog(@"%f",distance);
    
    
    UIBezierPath *path = [self pathWithSmallCircle:self.smallCircle BigCirCle:self];
    
    // 形狀圖層
    if (self.smallCircle.hidden == NO) {
        self.shapL.path = path.CGPath;
    }

    if (distance > 60) {
        //讓小圓隱藏,讓路徑隱藏
        self.smallCircle.hidden = YES;
        [self.shapL removeFromSuperlayer];
    }
    
    if (pan.state == UIGestureRecognizerStateEnded) {
        
        //判斷距離是否大於60.
        //大於60讓按鈕消失
        if(distance < 60) {
          //小於60,復位
            [self.shapL removeFromSuperlayer];
            self.center = self.smallCircle.center;
            
            self.smallCircle.hidden = NO;
        }else {
            
            //播放一個動畫消失
            UIImageView *imageV = [[UIImageView alloc] initWithFrame:self.bounds];
            
            NSMutableArray *imageArray = [NSMutableArray array];
            for (int i = 0 ; i < 8; i++) {
               UIImage *image =  [UIImage imageNamed:[NSString stringWithFormat:@"%d",i +1]];
                [imageArray addObject:image];
            }
            
            imageV.animationImages = imageArray;
            imageV.animationDuration = 1;
            [imageV startAnimating];
            
            [self addSubview:imageV];
            
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self removeFromSuperview];
            });
            
            
        }
      
        
        
    }
    
    
    
    
    
}

//給定兩個圓,描述一個不規則的路徑
- (UIBezierPath *)pathWithSmallCircle:(UIView *)smallCircle BigCirCle:(UIView  *)bigCircle {
    
    
    CGFloat x1 = smallCircle.center.x;
    CGFloat y1 = smallCircle.center.y;
    
    CGFloat x2 = bigCircle.center.x;
    CGFloat y2 = bigCircle.center.y;
    
    CGFloat d = [self distanceWithSmallCircle:smallCircle BigCirCle:bigCircle];
    
    if (d <= 0) {
        return nil;
    }
    
    
    CGFloat cosθ = (y2 - y1) / d;
    CGFloat sinθ = (x2 - x1) / d;
    
    CGFloat r1 = smallCircle.bounds.size.width * 0.5;
    CGFloat r2 = bigCircle.bounds.size.width * 0.5;
    
    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];
    
    
    return path;
    
    
}


//求兩個圓之間距離
- (CGFloat)distanceWithSmallCircle:(UIView *)smallCircle BigCirCle:(UIView  *)bigCircle {
    
    //x軸方法向的偏移量
    CGFloat offsetX = bigCircle.center.x - smallCircle.center.x;
    //y軸方法向的偏移量
    CGFloat offsetY = bigCircle.center.y - smallCircle.center.y;
    
    return  sqrt(offsetX * offsetX + offsetY * offsetY);
}





- (void)setUp {
    
    //圓角
    self.layer.cornerRadius = self.bounds.size.width * 0.5;
    //設置背景顏色
    [self setBackgroundColor:[UIColor redColor]];
    [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    self.titleLabel.font = [UIFont systemFontOfSize:12];
    
    
    //添加小圓
    UIView *smallCircle = [[UIView alloc] initWithFrame:self.frame];
    smallCircle.layer.cornerRadius = self.layer.cornerRadius;
    smallCircle.backgroundColor = self.backgroundColor;
    
    [self.superview addSubview:smallCircle];
    self.smallCircle = smallCircle;
    
    //把一個UIView添加到指定的位置
    [self.superview insertSubview:smallCircle belowSubview:self];

    
}

//取消高亮狀態
-(void)setHighlighted:(BOOL)highlighted {

}


@end

三.控制器.m代碼如下:

//  ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    
    self.view.translatesAutoresizingMaskIntoConstraints = NO;
    
}

@end


效果圖如下:




筆記:

整體思路徑: 指移動,按鈕跟着移動.按鈕跟着 指移動.移動時底部有 個圓,

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

指移動超出 定的範圍,填充效果消失,當 指鬆開時.判斷當前 圓距離與 圓之間的距離.

如果 於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:selfaction:@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; 


粘性計算圖:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章