展示效果
實現效果
實現思路
- 2個圓(一個是固定圓,一個是可拖動的圓)
- 貝塞爾畫形狀
- 拖動的時候固定圓的比例是縮小的
- 到一定距離的時候會斷開
- 松開手勢就會回彈到原地
事前準備工作
-
屬性的創建
@property (nonatomic, strong) UIView *view1;
@property (nonatomic, strong) UIView *view2;
@property (nonatomic, strong) CAShapeLayer *shapeLayer;
@property (nonatomic, assign) CGPoint oldViewCenter;
@property (nonatomic, assign) CGRect oldViewFram;
@property (nonatomic, assign) CGFloat r1;
-
核心代碼
- (void)setup{
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetWidth(self.view.bounds) / 2, CGRectGetHeight(self.view.bounds) * 1/9, 40, 40)];
view1.backgroundColor = [UIColor redColor];
view1.layer.cornerRadius = 20;
_view1 = view1;
[self.view addSubview:view1];
UIView *view2 = [[UIView alloc] initWithFrame:_view1.frame];
view2.backgroundColor = [UIColor redColor];
view2.layer.cornerRadius = 20;
_view2 = view2;
[self.view addSubview:view2];
//添加label
UILabel *numL = [[UILabel alloc] initWithFrame:_view2.bounds];
numL.text = @"66";
numL.textAlignment = NSTextAlignmentCenter;
numL.textColor = [UIColor whiteColor];
[_view2 addSubview:numL];
//添加手勢
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
[_view2 addGestureRecognizer:pan];
//實例化CAShapeLayer
_shapeLayer = [CAShapeLayer layer];
_shapeLayer.fillColor = [UIColor redColor].CGColor;
// [self.view.layer addSublayer:_shapeLayer];
_oldViewFram = _view1.frame;
_oldViewCenter = _view1.center;
_r1 = CGRectGetWidth(_view1.frame)/2;
}
-
避坑指南
- 需要保存原先中心點的位置
- 需要保存原先圓的大小,因為在斷開的時候需要回到原來的樣子
- shapeLayer 是直接初始化添加,在拖拽中改變path就可以了
-
根據手勢判斷處理移動View屬性狀態
- (void)panAction:(UIPanGestureRecognizer *)ges{
if (ges.state == UIGestureRecognizerStateChanged) {
//view2跟著移動
_view2.center = [ges locationInView:self.view];
//
if (_r1 < wift
_view1.hidden = YES;
[_shapeLayer removeFromSuperlayer];
}else{
//計算出6個關鍵點,然后畫貝塞爾曲線
[self caculPoint];
}
}else if (ges.state == UIGestureRecognizerStateFailed || ges.state == UIGestureRecognizerStateEnded || ges.state == UIGestureRecognizerStateCancelled){
[_shapeLayer removeFromSuperlayer];
[UIView animateWithDuration:.5 delay:0 usingSpringWithDamping:.3 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
_view2.center = _oldViewCenter;
} completion:^(BOOL finished) {
_view1.hidden = NO;
_r1 = _oldViewFram.size.width/2;
_view1.frame = _oldViewFram;
_view1.layer.cornerRadius = _r1;
}];
}
}
-
避坑指南
- 因為在結束動畫之后會需要很多點的復原問題,所以我們在開始的時候就需要記錄
- [_shapeLayer removeFromSuperlayer]; 在取消和結束的時候還是需要移除layer的
不然會出現畫的紅色背景還在的情況- usingSpringWithDamping 彈簧阻力效果
-
計算拖拽中的關鍵點
- (void)caculPoint{
//1.求出2個中心點
CGPoint center1 = _view1.center;
CGPoint center2 = _view2.center;
//2.計算2個中心點的距離
CGFloat dis = sqrtf((center1.x - center2.x)*(center1.x - center2.x) + (center1.y - center2.y)*(center1.y - center2.y));
//3.計算正弦余弦
CGFloat sin = (center2.x - center1.x) / dis;
CGFloat cos = (center1.y - center2.y) / dis;
//4.計算半徑
CGFloat r1 = CGRectGetWidth(_oldViewFram)/2 - dis/20;
CGFloat r2 = CGRectGetWidth(_view2.bounds)/2;
_r1 = r1;
NSLog(@"%f",r1);
//5.計算6個關鍵點
CGPoint pA = CGPointMake(center1.x - cos*r1, center1.y - sin*r1);
CGPoint pB = CGPointMake(center1.x + cos*r1, center1.y + sin*r1);
CGPoint pD = CGPointMake(center2.x - cos*r2, center2.y - sin*r2);
CGPoint pC = CGPointMake(center2.x + cos*r2, center2.y + sin*r2);
CGPoint pP = CGPointMake(pB.x + dis/2*sin, pB.y - dis/2*cos);
CGPoint pO = CGPointMake(pA.x + dis/2*sin, pA.y - dis/2*cos);
//6.畫貝塞爾曲線
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:pA];
[path addQuadCurveToPoint:pD controlPoint:pO];
[path addLineToPoint:pC];
[path addQuadCurveToPoint:pB controlPoint:pP];
[path closePath];
//7.修改_shapeLayer的path
_shapeLayer.path = path.CGPath;
[self.view.layer insertSublayer:_shapeLayer below:_view2.layer];
//8.重新設置view的大小
_view1.center = _oldViewCenter;
_view1.bounds = CGRectMake(0, 0, _r1*2, _r1*2);
_view1.layer.cornerRadius = _r1;
}
-
避坑指南
- CGRectGetWidth(_oldViewFram)/2 - dis/20; 這里再次設置寬度的時候需要用到原先的那個值,不然r1的值就會有問題
-
關鍵點計算圖示
6個關鍵點計算說明(ABCDOP)