因为项目中需要一个消息提醒,自己学习了一下,然后写了这篇博客,为了自己平时没事的时候可以看一下。代码基本每一行都进行了注释。如果有不对的地方,谢谢指教
不多说了,直接说怎么实现。
实现思路:
1, 新建一个Button类,设置button为圆角。
2,为Button添加 UIPanGestureRecognizer平移手势
3,根据平移手势,设置移动视图所在视图中的移动位置
4,根据移动距离设置触发事件,如果移动距离小于设置移动距离,设置移动后原点的小球,
根据贝赛尔曲线绘制小球移动路径。若移动距离大于设置距离,移除原点小球,移动路径
5,手势结束,若移动距离小于设置距离,还原,移除移动后原点显示小球,路径。若移动距离大于设置距离,移除所有
下面直接上源代码,代码有详细解释,如有不对,不好之处,谢谢指教:
代码部分:
#import "SmallBallButton.h"
@interface SmallBallButton()<UIGestureRecognizerDelegate>
//小圆
@property (strong,nonatomic)UIView *smallCircleView;
//轨迹layer,CALayer的子类
/CALayer的子类。绘图
///http://www.cocoachina.com/ios/20160214/15251.html对CAShapeLayer的解释
@property (strong,nonatomic)CAShapeLayer *shapeLayer;
//轨迹,贝塞尔曲线获取
@property (strong,nonatomic)UIBezierPath *path;
///图片数组
@property (strong,nonatomic)NSMutableArray *images;
@end
@implementation SmallBallButton
#pragma mark-懒加载在使用的时候初始化
-(UIView*)smallCircleView{
if (!_smallCircleView) {
_smallCircleView = [UIViewnew];
}
return_smallCircleView;
}
-(CAShapeLayer*)shapeLayer{
if (!_shapeLayer) {
_shapeLayer = [CAShapeLayernew];
}
return_shapeLayer;
}
-(NSMutableArray*)images{
if (!_images) {
_images = [NSMutableArrayarray];
for (inti=1;i<9; i++) {
UIImage * image = [UIImageimageNamed:[NSStringstringWithFormat:@"%d",i]];
[self.imagesaddObject:image];
}
}
return_images;
}
///更新UI
/*layoutSubviews在以下情况下会被调用:
*1、init初始化不会触发layoutSubviews但是是用initWithFrame进行初始化时,当rect的值不为CGRectZero时,也会触发
*2、addSubview会触发layoutSubviews
*3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
*4、滚动一个UIScrollView会触发layoutSubviews
*5、旋转Screen会触发父UIView上的layoutSubviews事件
*6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
*/
-(void)layoutSubviews{
[superlayoutSubviews];
[selfinitAll];
}
-(void)initAll{
///拖拽距离,默认100像素
if(_maxDistance ==0){
_maxDistance =100;
}
///初始化Btn属性
///设置圆角
self.layer.cornerRadius =self.bounds.size.width/2;
self.layer.masksToBounds =YES;
///点击事件
[selfaddTarget:selfaction:@selector(clickBtn)forControlEvents:UIControlEventTouchUpInside];
///移动后原点小球
///bounds
self.smallCircleView.bounds = self.bounds;
///中心点
self.smallCircleView.center = self.center;
self.smallCircleView.backgroundColor = self.backgroundColor;
self.smallCircleView.layer.cornerRadius = _smallCircleView.bounds.size.width/2;
///添加到Button底部
[self.superviewinsertSubview:self.smallCircleViewbelowSubview:self];
///平移手势
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(panAction:)];
[selfaddGestureRecognizer:pan];
}
//在平移手势事件中设置平移后圆的位置
-(void)panAction:(UIPanGestureRecognizer*)panGesture{
获取所在视图中的平移手势,获取手势初始 x,y相对坐标
CGPoint point = [panGesturetranslationInView:self.superview];
///获取手势移动的x,y,sender.view.center.x是相对于所在视图的X point.x是每次移动X即移动横坐标。Y同
CGFloat centerX = panGesture.view.center.x+ point.x;
CGFloatcenterY = panGesture.view.center.y+ point.y;
///设置视图移动后所在视图的位置
panGesture.view.center=CGPointMake(centerX, centerY);
在所在视图中设置平移值,设置影响平移速度
[panGesture setTranslation:CGPointMake(0,0)inView:self.superview];
//计算移动距离
CGFloat distance = [selfgetDistanceFromPointA:self.centerToPointB:_smallCircleView.center];
if (distance <_maxDistance) {
///每当distance < _maxDistance,都设置_smallCircleView不隐藏
_smallCircleView.hidden =NO;
///根据按钮大小设置小球的大小,这里设置初始值为按钮大小的一半
CGFloat radius = panGesture.view.bounds.size.width> panGesture.view.bounds.size.height? panGesture.view.bounds.size.width*0.5:panGesture.view.bounds.size.height*0.5;
///设置小球的大小,根据距离改变大小
_smallCircleView.bounds =CGRectMake(0,0, radius-distance/10, radius-distance/10);
///根据大小设置圆角
_smallCircleView.layer.cornerRadius= (radius-distance/10)*0.5;
if(_smallCircleView.hidden==NO&& distance>0){
///设置填充颜色
_shapeLayer.fillColor =self.backgroundColor.CGColor;
///设置贝塞尔曲线绘图路径
self.shapeLayer.path = [self pathWithBigCirCleView:selfsmallCirCleView:_smallCircleView].CGPath;
将绘制图形插入视图
[self.superview.layerinsertSublayer:_shapeLayerbelow:_smallCircleView.layer];
}
}else{
[_shapeLayerremoveFromSuperlayer];
_shapeLayer =nil;
_smallCircleView.hidden =YES;
}
///如果平移结束
if(panGesture.state ==UIGestureRecognizerStateEnded){
if(distance >_maxDistance){
NSLog(@"拖动 ->销毁了");
[selfstartDestroyAnimations];
[selfallKill];
}else{
[_shapeLayerremoveFromSuperlayer];
_shapeLayer =nil;
///设置弹簧效果动画,效果UIViewAnimationOptionCurveEaseInOut缓慢->加速->缓慢
[UIViewanimateWithDuration:0.3delay:0usingSpringWithDamping:0.2initialSpringVelocity:1options:UIViewAnimationOptionCurveEaseInOutanimations:^{
///还原
self.center =_smallCircleView.center;
} completion:^(BOOLfinished) {
_smallCircleView.hidden =NO;
}];
}
}
}
///计算相对距离
-(CGFloat)getDistanceFromPointA:(CGPoint)PointA ToPointB:(CGPoint)PointB{
CGFloat x = PointA.x-PointB.x;
CGFloaty = PointA.y- PointB.y;
returnsqrtf(x*x + y*y);
}
按钮点击事件
-(void)clickBtn{
self.clickName(self);
[selfstartDestroyAnimations];
[selfallKill];
}
///UIBezierPath 顾名思义,这是用贝塞尔曲线的方式来构建一段弧线
-(UIBezierPath*)pathWithBigCirCleView:(UIView*)bigCirCleView smallCirCleView:(UIView*)smallCirCleView
{
//获取button的相对坐标x,y
CGPoint bigCenter = bigCirCleView.center;
CGFloat bigX = bigCenter.x;
CGFloat bigY = bigCenter.y;
//根据bounds设置大球的圆角
CGFloat bigRadius = bigCirCleView.bounds.size.width*0.5;
///获取小球的相对坐标 x,y
CGPoint smallCenter = smallCirCleView.center;
CGFloat smallX = smallCenter.x;
CGFloatsmallY = smallCenter.y;
//根据bounds设置小球的圆角
CGFloat smallRadius = smallCirCleView.bounds.size.width*0.5;
///大小球距离
CGFloat d = [selfgetDistanceFromPointA:smallCenterToPointB:bigCenter];
///三角形bigX-smallX横线 bigY-smallY三角形竖线
CGFloat sina = (bigX-smallX)/d;
CGFloat cosa = (bigY-smallY)/d;
///计算小球中心点为原点第一象限的点
CGPoint pointA =CGPointMake(smallX-smallRadius*cosa, smallY+smallRadius*sina);
///计算小球中心店为原点第三象限的点
CGPoint pointB =CGPointMake(smallX+smallRadius*cosa, smallY-smallRadius*sina);
///同上,打球第一,第三象限的点(一:C,三:D)
CGPoint pointC =CGPointMake(bigX+bigRadius*cosa, bigY-bigRadius*sina);
CGPoint pointD =CGPointMake(bigX-bigRadius*cosa, bigY+bigRadius*sina);
//计算小球第四象限的点,(大概是距离大球最近的点)
CGPoint pointO =CGPointMake(pointA.x+d/2*sina, pointA.y+d/2*cosa);
//计算大球第四象限的点(大概是距离小球最远的点)
CGPoint pointP =CGPointMake(pointB.x+d/2*sina, pointB.y+d/2*cosa);
//使用UIBezierPath类可以创建基于矢量的路径,这个类在UIKit中。此类是Core Graphics框架关于path的一个封装。使用此类可以定义简单的形状,如椭圆或者矩形,或者有多个直线和曲线段组成的形状贝赛尔曲线
UIBezierPath *path = [UIBezierPathbezierPath];
// A 设置初始线段的起点
[path moveToPoint:pointA];
// AB 设置先前的终点,之后的起点
[path addLineToPoint:pointB];
// 绘制BC曲线,起点B
[path addQuadCurveToPoint:pointCcontrolPoint:pointP];
// CD 绘制 C到D的直线
[path addLineToPoint:pointD];
// 绘制DA曲线
[path addQuadCurveToPoint:pointAcontrolPoint:pointO];
return path;
}
移除动画(UIImageView的动画,连续播放图片组成动画)
-(void)startDestroyAnimations
{
UIImageView *animaImageView = [[UIImageViewalloc]initWithFrame:self.frame];
///设置用作动画的图片数组
animaImageView.animationImages=self.images;//images是装image的数组
///指定重复动画的次数。默认值为0,指定无限期重复动画。
animaImageView.animationRepeatCount=1;
///动画时间
animaImageView.animationDuration=0.5;
开始动画
[animaImageView startAnimating];
///添加到使用控制器
[self.superviewaddSubview:animaImageView];
}
///移除所有
-(void)allKill
{
[selfremoveFromSuperview];
[_smallCircleViewremoveFromSuperview];
[_shapeLayerremoveFromSuperlayer];
_smallCircleView =nil;
_shapeLayer =nil;
}
@end
效果图:
效果图:
项目代码:点我下载Demo