quartz 2d

quartz 2d的知识我总是容易忘记,在这里打算花点时间整理一下,对自己和对大家都是有好处的,如果我有什么不对或者不全的地方 请大家给予指正,鄙人不胜感激。

画一条直线

绘图的基本步骤
  • 创建上下文(这个必须在drawRect方法才可以 ,默认view 调用一次,每次调用setNeedsDisplay 它会调用一次)
  • 对- (void)drawRect:(CGRect)rect说明
    1.调用时机:是在view加载完毕的时候开始调用
    2.rect 就是view的bounds
    3.用来专门绘制上下文的
  • 上下文步骤的详细说明
    1.创建上下文
    2.创建路径
    3.将路径添加到上下文
    4.开始绘制
  • 画一条直线的详细代码
- (void)drawRect:(CGRect)rect {
    // 获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 创建路径
    UIBezierPath *path = [[UIBezierPath alloc] init];
    // 创建起点
    [path moveToPoint:CGPointMake(10, 100)];
    // 创建终点
    [path addLineToPoint:CGPointMake(100, 10)];
    // 在加上一个终点
    [path addLineToPoint:CGPointMake(180, 100)];
    // 设置线宽
    CGContextSetLineWidth(ctx, 10);
    // 设置颜色
    [[UIColor greenColor] setStroke];
    // 添加圆角(在两条线连接的地方)
    CGContextSetLineJoin(ctx, kCGLineJoinRound);
    // 设置两条线的尾部的圆角
    CGContextSetLineCap(ctx, kCGLineCapRound);
    // 将路径添加到上下文上
    CGContextAddPath(ctx, path.CGPath);
    // 进行绘制
    CGContextStrokePath(ctx);
}
1.png
  • 画一条曲线
- (void)drawRect:(CGRect)rect {
    // 获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 创建路径
    UIBezierPath *path = [[UIBezierPath alloc] init];
    //创建起始点
    [path moveToPoint:CGPointMake(10, 100)];
    // 画曲线
    [path addQuadCurveToPoint:CGPointMake(180, 100) controlPoint:CGPointMake(100, 0)];
    // 设置颜色
    [[UIColor greenColor] setStroke];
    // 设置线宽
    CGContextSetLineWidth(ctx, 10);
    // 设置两条线的尾部的圆角
    CGContextSetLineCap(ctx, kCGLineCapRound);
    // 将路径添加到上下文上
    CGContextAddPath(ctx, path.CGPath);
    // 进行绘制
    CGContextStrokePath(ctx);
}
2.png
  • 画一个长方形或者正方形
- (void)drawRect:(CGRect)rect {
    // 用这个方法创建的时候已经默认创建了上下文
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 100, 100)];
    // 设置四个角的样式
    path.lineJoinStyle = kCGLineJoinRound;
    // 设置颜色
    [[UIColor greenColor] setStroke];
    // 设置边角宽度
    path.lineWidth = 10.0;
    // 开始绘制
    [path stroke];
}
2.png
  • 画一个圆形
- (void)drawRect:(CGRect)rect {
    
    // 用这个方法创建的时候已经默认创建了上下文
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10, 10, 100, 100) cornerRadius:50];
    // 设置四个角的样式
    path.lineJoinStyle = kCGLineJoinRound;
    // 设置颜色
    [[UIColor greenColor] setStroke];
    // 设置边角宽度
    path.lineWidth = 10.0;
    // 开始绘制
    [path stroke];

} 
1.png
  • 画一个实心圆 用另外的一种方法
- (void)drawRect:(CGRect)rect {
   
    // 创建上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // frame
    CGRect frame = CGRectMake(10, 10, 100, 100);
    // 设置颜色
    [[UIColor greenColor] set];
    // 画一个圆形的区域
    CGContextAddEllipseInRect(ctx, frame);
    // 开始绘制
    CGContextFillPath(ctx);

} 
1.png
  • 画一个椭圆(空心的)
- (void)drawRect:(CGRect)rect {
    // 创建路径
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 180, 100)];
    // 设置宽度
    path.lineWidth = 10;
    // 设置颜色
    [[UIColor greenColor] setStroke];
    // 开始绘制
    [path stroke];
}
空心的椭圆.png
  • 画一个实心的椭圆
/**
 画一个实心的椭圆
 */
- (void)drawRealOval {
    // 创建路径
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 180, 100)];
    // 设置颜色
    [[UIColor greenColor] setFill];
    // 开始绘制
    [path fill];
}
实心的椭圆.png
  • 画一个空心的圆弧
/**
 画一个空心的圆弧
 */
-(void)drawArc {
    // 开始的角度
    CGFloat startA = -M_PI_2;
    // 结束的角度
    CGFloat endA = M_PI_2;
    // 开始创建路径(clockwise : yes  是顺时针 no:逆时针)
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:80 startAngle:startA endAngle:endA clockwise:YES];
    // 设置颜色
    [[UIColor greenColor] setStroke];
    // 宽度
    path.lineWidth = 10;
    // 开始绘制
    [path stroke];
}
空心的圆弧.png
  • 画一个实心的圆弧
/**
 画一个实心的圆弧
 */
- (void)drawRealArc {
    // 开始的角度
    CGFloat startA = -M_PI_2;
    // 结束的角度
    CGFloat endA = M_PI_2;
    // 开始创建路径(clockwise : yes  是顺时针 no:逆时针)
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:80 startAngle:startA endAngle:endA clockwise:YES];
    // 设置颜色
    [[UIColor greenColor] setFill];
    // 开始绘制
    [path fill];
}
实心圆弧.png
  • 画一个字符串( [str drawAtPoint:CGPointZero withAttributes:dic];其中它与 [str drawInRect:rect withAttributes:dic]的区别是drawInRect:rect可以换行的)
- (void)drawRect:(CGRect)rect {
    NSString *str = @"哈哈哈哈";
    NSShadow *shadow = [[NSShadow alloc] init];
    // 宽高的偏移
    shadow.shadowOffset = CGSizeMake(10, 10);
    // 暗影颜色
    shadow.shadowColor = [UIColor greenColor];
    // 模糊
    shadow.shadowBlurRadius = 5;
    NSDictionary *dic = @{
                          NSFontAttributeName:[UIFont systemFontOfSize:30],
                          NSForegroundColorAttributeName : [UIColor whiteColor],
                          NSShadowAttributeName : shadow
                          };
    [str drawAtPoint:CGPointZero withAttributes:dic];
   
}
1.png
  • 画文字 2( [str drawInRect:rect withAttributes:dic];)
- (void)drawRect:(CGRect)rect {
    
    NSString *str = @"哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈";
    NSShadow *shadow = [[NSShadow alloc] init];
    // 宽高的偏移
    shadow.shadowOffset = CGSizeMake(10, 10);
    // 暗影颜色
    shadow.shadowColor = [UIColor greenColor];
    // 模糊
    shadow.shadowBlurRadius = 5;
    NSDictionary *dic = @{
                          NSFontAttributeName:[UIFont systemFontOfSize:30],
                          NSForegroundColorAttributeName : [UIColor whiteColor],
                          NSShadowAttributeName : shadow
                          };
    [str drawInRect:rect withAttributes:dic];
}
2.png
  • image的绘制
- (void)drawRect:(CGRect)rect {
   
    UIImage *image = [UIImage imageNamed:@"1"];
    // 这个可以把一整张图片显示完全
    [image drawInRect:rect];
}
2.png
  • image的另外一个显示方法
- (void)drawRect:(CGRect)rect {
    UIImage *image = [UIImage imageNamed:@"1"];
    // 显示不完全 按照实际的比例进行显示
    [image drawAtPoint:CGPointZero];
}
3.png
  • image 平铺的方式
- (void)drawRect:(CGRect)rect {
    UIImage *image = [UIImage imageNamed:@"美眉"];
    // 显示不完全 按照实际的比例进行显示
    [image drawAsPatternInRect:rect];
}
4.png
  • 接下来模仿一下系统的imageview的方法,具体代码我会写详细的注释,为了自己以后忘记的时候能够回来看看
    1)写一个自定义的view(LDGImageView.h 文件)
#import <UIKit/UIKit.h>

@interface LDGImageView : UIView

/** image*/
@property (nonatomic, strong) UIImage *image;
/**
 初始化

 @param image image
 @return 对象本身
 */
- (instancetype)initWithImage:(UIImage *)image;

@end

2)LDGImageView.m 文件

#import "LDGImageView.h"

@implementation LDGImageView

-(void)setImage:(UIImage *)image {
    _image = image;
    [self setNeedsDisplay];
}
/**
 初始化
 
 @param image image
 @return 对象本身
 */
- (instancetype)initWithImage:(UIImage *)image {
    if (self = [super init]) {
        self.image = image;
        self.frame = CGRectMake(0, 0, image.size.width, image.size.height);
        [self setNeedsDisplay];
    }
    return self;
    
}
- (void)drawRect:(CGRect)rect {
    [self.image drawInRect:rect];
    
}

3)viewController的使用
3.1)普通方法的调用

 LDGImageView *imageView = [[LDGImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    imageView.image = [UIImage imageNamed:@"1"];
    [self.view addSubview:imageView];
7.png

3.2) initimage方法的调用

 LDGImageView *imageView = [[LDGImageView alloc] initWithImage:[UIImage imageNamed:@"1"]];
    [self.view addSubview:imageView];
5.png

3.3) 切换image也是可以的,这里不做演示了。

  • 图形上下文的状态栈
- (void)drawRect:(CGRect)rect {
    //  创建上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //  创建路径
    UIBezierPath *path = [UIBezierPath bezierPath];
    //  创建一条直线
    [path moveToPoint:CGPointMake(10, 100)];
    [path addLineToPoint:CGPointMake(190, 100)];
    //  将当前的上下文添加到上下文的栈里
    CGContextSaveGState(ctx);
    //  设置颜色和宽度
    [[UIColor greenColor] setStroke];
    CGContextSetLineWidth(ctx, 10);
    //  将路径添加到上下文上
    CGContextAddPath(ctx, path.CGPath);
    //  开始绘制
    CGContextStrokePath(ctx);
    
    // 从新初始化路径
    path = [UIBezierPath bezierPath];
    // 恢复最开始的状态
    CGContextRestoreGState(ctx);
    // 绘制路径
    [path moveToPoint:CGPointMake(100, 10)];
    [path addLineToPoint:CGPointMake(100, 190)];
    //  设置颜色和宽度
    [[UIColor blueColor] setStroke];
    CGContextSetLineWidth(ctx, 5);
    // 将路径添加到上下文上
    CGContextAddPath(ctx, path.CGPath);
    // 开始绘制
    CGContextStrokePath(ctx);
}
1.png
  • 图形上下文的矩阵操作
    1.1) 平移
- (void)drawRect:(CGRect)rect {
    // 创建上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 创建一个实心圆
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 50, 50)];
    // 设置颜色
    [[UIColor greenColor] setFill];
    // 平移
    CGContextTranslateCTM(ctx, 100, 120);
    // 开始绘制
    [path fill];
}
1.png

1.2)旋转

- (void)drawRect:(CGRect)rect {
    
    // 创建上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 创建一个实心圆
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 100, 50)];
    // 设置颜色
    [[UIColor greenColor] setFill];
    // 旋转
    CGContextRotateCTM(ctx, M_PI_4);
    // 开始绘制
    [path fill];
 
}
2.png

1.3)缩放大小

- (void)drawRect:(CGRect)rect {
    
    // 创建上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 创建一个实心圆
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 100, 50)];
    // 设置颜色
    [[UIColor greenColor] setFill];
    // 缩放
    CGContextScaleCTM(ctx, 2.0, 2.0);
    // 开始绘制
    [path fill];
}
3.png
  • 一个图片水印的例子,不用在drawrect方法里边实现了
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 开启图片上下文
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 0.0);
    // 创建image
    UIImage *image = [UIImage imageNamed:@"美眉"];
    [image drawInRect:CGRectMake(0, 0, 200, 200)];
    // 创建logo文字
    NSString *str = @"阿斯顿哈说的";
    [str drawAtPoint:CGPointMake(20, 20) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0],
                                                          NSForegroundColorAttributeName :[UIColor redColor]
                                                          }];
    // 获得上下文生成的图片
    UIImage *getImage = UIGraphicsGetImageFromCurrentImageContext();
    // 结束当前图片上下文
    UIGraphicsEndImageContext();
    
    self.displayImageView.image = getImage;

}
4.png
  • 一个带有边框的圆形片的裁剪(给image加的分类)
/**
 裁剪一个图片为圆形
 
 @param borderWidth 边框宽度
 @param borderColor 边框颜色
 @param imageViewWidth 外面的imageview的宽度,如果为0 将按照比例显示不按照正确的显示,如果imageview不是正方形请按照最小的传递
 @param imageName 图片的名字
 @return 想要的图片
 */
+(UIImage *)imageWithBorderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor imageViewWidth:(CGFloat)imageViewWidth imageName:(NSString *)imageName {
    
    if (imageName.length == 0) {
        return nil;
    }
    UIImage *image = [UIImage imageNamed:imageName];
    CGFloat imageWidth = image.size.width;
    CGFloat imageHeight = image.size.height;
    imageWidth < imageHeight ? (imageHeight = imageWidth) : (imageWidth = imageHeight);
    // 这里为了精确显示传入进来的borderwidth,计算比例
    if (imageViewWidth > 0) {
        CGFloat scale = 1.0 * (imageWidth + 2*borderWidth)/imageViewWidth;
        if (scale < 1.0) {
            scale = 1.0 * imageWidth/imageViewWidth;
        }
        borderWidth = scale * borderWidth;
    }
    // 创建图片上下文
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(imageWidth + 2*borderWidth, imageHeight + 2*borderWidth), NO, 0.0);
    // 创建外面的大圆
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, imageWidth + 2*borderWidth, imageHeight + 2*borderWidth)];
    // 绘制颜色
    [borderColor set];
    // 填写
    [path fill];
    // 创建小圆
    CGRect rect = CGRectMake(borderWidth, borderWidth, imageWidth, imageHeight);
    path = [UIBezierPath bezierPathWithOvalInRect:rect];
    // 添加裁剪
    [path addClip];
    // 图片绘制
    [image drawInRect:rect];
    // 获取新的图片
    UIImage *getImage = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭图片上下文
    UIGraphicsEndImageContext();
    return getImage;
}
图片本身大于imageview.png
图片本身小于imageview.png
  • 截屏
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 开启图片上下文
    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0.0);
    // 获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 进行绘制
    [self.view.layer renderInContext:ctx];
    // 获取图片
    UIImage *getImage = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭上下文
    UIGraphicsEndImageContext();
    // 保持原比例
    NSData *data = UIImageJPEGRepresentation(getImage, 1.0);
    [data writeToFile:@"/Users/apple/Desktop/l.jpg" atomically:YES];
}
1.png
  • 图片截屏
/**
 拖拽事件

 @param sender 首饰
 */
- (IBAction)panGesture:(UIPanGestureRecognizer *)sender {
    if (sender.state == UIGestureRecognizerStateBegan) {
        self.startP = [sender locationInView:self.view];
        
    }else if (sender.state == UIGestureRecognizerStateChanged){
        CGPoint currentP = [sender locationInView:self.view];
        CGFloat width = currentP.x - self.startP.x;
        CGFloat height = currentP.y - self.startP.y;
        self.corverView.frame = CGRectMake(self.startP.x, self.startP.y, width, height);
    }else if (sender.state == UIGestureRecognizerStateEnded){
        // 开启图片的上下文
        UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0.0);
        // 创建路径
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.corverView.frame];
        // 超出部分进行裁剪
        [path addClip];
        // 拿到当前的上下文
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        // 开始绘制
        [self.displayImageView.layer renderInContext:ctx];
        // 获得图片
        UIImage *getImage = UIGraphicsGetImageFromCurrentImageContext();
        // 关闭上下文
        UIGraphicsEndImageContext();
        self.displayImageView.image = getImage;
        // 移除覆盖的view
        [self.corverView removeFromSuperview];
    }
}
1.png
2.png
  • 图片擦除
- (IBAction)panGesture:(UIPanGestureRecognizer *)sender {
    
    // 擦除的宽度
    CGFloat width = 50;
    CGPoint currrentP = [sender locationInView:self.view];
    CGFloat pointX = currrentP.x - width *0.5;
    CGFloat pointY = currrentP.y - width *0.5;
    // 创建图片上下文
    UIGraphicsBeginImageContextWithOptions(self.displayImageView.bounds.size, NO, 0.0);
    // 获取当前的上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 进行绘制
    [self.displayImageView.layer renderInContext:ctx];
    // 擦除区域
    CGRect clearRect = CGRectMake(pointX, pointY, width, width);
    // 进行擦除
    CGContextClearRect(ctx, clearRect);
    // 生成新的图片
    UIImage *getImage = UIGraphicsGetImageFromCurrentImageContext();
    // 结束图片的上下文
    UIGraphicsEndImageContext();
    self.displayImageView.image = getImage;
1.png
  • 一个手势解锁的例子(写的一般有时间在优化)
#import "GestureView.h"
#define ButtonW 70
#define ButtonLeftMargen 30
#define ButtonTopMargen 30
#define ButtonCommonMargen 10

@interface GestureView ()

@property (strong, nonatomic) NSMutableArray *buttonArray;
@property (assign, nonatomic) CGPoint currentP;

@end
@implementation GestureView

-(NSMutableArray *)buttonArray {
    if (!_buttonArray) {
        _buttonArray = [NSMutableArray array];
    }
    return _buttonArray;
}
- (void)awakeFromNib {
    [super awakeFromNib];
    // 创建button
    for (NSInteger index = 0; index < 9; index ++) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button setImage:[UIImage imageNamed:@"gesture_normal"] forState:UIControlStateNormal];
        [button setImage:[UIImage imageNamed:@"gesture_selected"] forState:UIControlStateSelected];
        button.tag = index;
        button.userInteractionEnabled = NO;
        [self addSubview:button];
    }
}
/**
 改变一些frame
 */
- (void)layoutSubviews {
    [super layoutSubviews];
    // 在这里正式的设置button
    for (NSInteger index = 0; index < self.subviews.count; index++) {
        UIButton *button = (UIButton *)self.subviews[index];
        CGFloat buttonL = (index % 3)*(ButtonW + ButtonCommonMargen);
        CGFloat buttonT = (index / 3)*(ButtonW + ButtonCommonMargen);
        button.frame = CGRectMake(ButtonLeftMargen + buttonL, ButtonTopMargen + buttonT, ButtonW, ButtonW);
    }
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    CGPoint currentP = [self getPointWithTouchs:touches];
    UIButton *button = [self getButtonThroughPoint:currentP];
    if (button && button.selected == NO) {
        button.selected = YES;
        [self.buttonArray addObject:button];
        [self setNeedsDisplay];
    }
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    CGPoint currentP = [self getPointWithTouchs:touches];
    UIButton *button = [self getButtonThroughPoint:currentP];
    if (button && button.selected == NO) {
        button.selected = YES;
        [self.buttonArray addObject:button];
        
    }
    self.currentP = currentP;
    [self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSString *str = [[NSString alloc] init];
    for (UIButton *button in self.buttonArray) {
        button.selected = NO;
        str = [str stringByAppendingString:[NSString stringWithFormat:@"%zd",button.tag]];
    }
    NSLog(@"你想要的结果是 : %@",str);
    [self.buttonArray removeAllObjects];
    self.currentP = CGPointMake(0, 0);
    [self setNeedsDisplay];
    
    
    
}

/**
 通过一个点来获取button

 @param point 点
 @return 按钮
 */
- (UIButton *)getButtonThroughPoint:(CGPoint)point {
    for (UIButton *button in self.subviews) {
        if (CGRectContainsPoint(button.frame, point)) {
            return button;
        }
    }
    return nil;
}
/**
 通过touches来获得当前的点

 @param touches touches
 @return 获取的点
 */
- (CGPoint )getPointWithTouchs:(NSSet <UITouch *> *)touches {
    UITouch *touch = [touches anyObject];
    return [touch locationInView:self];
}

/**
 开始绘制

 @param rect 矩形
 */
- (void)drawRect:(CGRect)rect {
    
    if (self.buttonArray.count == 0) {
        return;
    }
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    for (NSInteger index = 0; index < self.buttonArray.count; index ++) {
        UIButton *button = (UIButton *)self.buttonArray[index];
        if (index == 0) {
            [path moveToPoint:button.center];
        }else {
            [path addLineToPoint:button.center];
        }
    }
    if (self.currentP.x > 0) {
       [path addLineToPoint:self.currentP];
    }
    [[UIColor greenColor] set];
    path.lineWidth = 5;
    [path setLineJoinStyle:kCGLineJoinRound];
    [path stroke];
    
}

@end
1.png
  • 涂鸦的功能
// drawView的.h 文件
#import <UIKit/UIKit.h>

@interface DrawView : UIView

/**
 显示的imageview
 */
@property (nonatomic,weak)IBOutlet UIImageView *displayImageView;
/**
 颜色
 */
@property (strong, nonatomic) UIColor *color;

/**
 宽度
 */
@property (assign, nonatomic) CGFloat width;
/**
  清除的操作
 */
- (void)clearAction;
/**
  撤销的操作
 */
- (void)undoAction;
/**
 橡皮擦的事件
 */
- (void)eraserAction;

@end
// drawView的.m文件

#import "DrawView.h"
#import "DrawBezierPath.h"

@interface DrawView ()

@property (strong, nonatomic) DrawBezierPath *drawPath;
@property (strong, nonatomic) NSMutableArray *pathArray;



@end
@implementation DrawView
-(NSMutableArray *)pathArray {
    if (!_pathArray) {
        _pathArray = [NSMutableArray array];
    }
    return _pathArray;
}
- (void)awakeFromNib {
    [super awakeFromNib];
    // 添加拖拽的事件
    UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
    [self addGestureRecognizer:panGes];
}

/**
 手势拖拽的响应

 @param panGesture 拖拽手势
 */
- (void)panAction:(UIPanGestureRecognizer *)panGesture{
    
    if (panGesture.state == UIGestureRecognizerStateBegan) {
        self.drawPath = [DrawBezierPath bezierPath];
        CGPoint currentPoint = [panGesture locationInView:self];
        [self.drawPath moveToPoint:currentPoint];
        _width > 0 ? (self.drawPath.lineWidth = _width):(self.drawPath.lineWidth = 1);
        _color != nil ? (self.drawPath.pathColor = _color) : (self.drawPath.pathColor = [UIColor blackColor]);
        [self.pathArray addObject:self.drawPath];
    }else if (panGesture.state == UIGestureRecognizerStateChanged){
        CGPoint currentPoint = [panGesture locationInView:self];
        [self.drawPath addLineToPoint:currentPoint];
    }
    [self setNeedsDisplay];
}

#pragma mark - 外部事件的响应
/**
 清除的操作
 */
- (void)clearAction{
    [self.pathArray removeAllObjects];
    [self setNeedsDisplay];
}
/**
 撤销的操作
 */
- (void)undoAction {
    [self.pathArray removeLastObject];
    [self setNeedsDisplay];
}
/**
 橡皮擦的事件
 */
- (void)eraserAction{
    self.color = self.backgroundColor;
    [self setNeedsDisplay];
}

/**
 设置颜色
 
 @param color 颜色
 */
- (void)setColor:(UIColor *)color{
    _color = color;
    [self setNeedsDisplay];
}
/**
 设置宽度
 
 @param width 宽度
 */
- (void)setWidth:(CGFloat)width{
    _width = width;
    [self setNeedsDisplay];
}

/**
 绘制上下文的部分

 @param rect 当前的上下文的区域
 */
- (void)drawRect:(CGRect)rect {
    
    for (DrawBezierPath *path in self.pathArray) {
        [path.pathColor set];
        [path stroke];
    }
}

@end
// DrawBezierPath.h文件 
#import <UIKit/UIKit.h>

@interface DrawBezierPath : UIBezierPath
@property (strong, nonatomic) UIColor *pathColor;


@end
// DrawBezierPath.m文件 
#import "DrawBezierPath.h"

@implementation DrawBezierPath

@end
1.png
  • CALayer
  • layer 和view的区别:
    1)view可以处理事件的响应,而layer是用来显示,不能处理事件
    2)每个UIView都有一个layer提供内容的绘制与显示,并且UIView的尺寸和样式都由CALayer所提供,两者都是树形结构,layer 有一个SubLayers view有一个SubViews,但是layer比view多一个anchorPoint
    3)其实个人理解view的frame都是由本身的layer的frame确定的
    4)看这个链接view和layer的区别
    //  阴影的透明度,默认是完全透明的也就是0
    self.redView.layer.shadowOpacity = 1.0;
    //  设置阴影的颜色
    self.redView.layer.shadowColor = [UIColor greenColor].CGColor;
    //  设置阴影的偏移量
    self.redView.layer.shadowOffset = CGSizeMake(50, -50);
    //  设置阴影的模糊程度
    self.redView.layer.shadowRadius = 10;
1.png
  • 两种方式显示图片
   // 第一种方式显示图片
    self.redView.layer.contents = (id)[UIImage imageNamed:@"timg"].CGImage;
    self.redView.layer.cornerRadius = 50;
    self.redView.layer.masksToBounds = YES;
    // 第二种方式显示图片
    self.displayImageView.layer.cornerRadius = 50;
    self.displayImageView.layer.masksToBounds = YES;
1.png
  • uiimageview也是继承view 为什么在下面的这段代码中 view就被裁剪了而imagview 没有变化 因为图片显示的是在layer的内容层上 第一个代码没有显示内容 所以可以被裁剪,如果两段代码都加上layer.masksToBounds = YES就起作用了 因为masksToBounds是裁剪根试图以及根上的所有的内容进行裁剪
    // 第一种方式显示,可以裁剪
    self.redView.layer.cornerRadius = 50;
    // 第二种方式显示
    self.displayImageView.layer.cornerRadius = 50;
1.png
  • 图片3d的平移、缩放、旋转
[UIView animateWithDuration:1.0 animations:^{
 // 第一种方式
        // 3d的类型缩放
        self.displayImageView.layer.transform = CATransform3DMakeScale(1.5, 1.8, 3.0);
        // 3d类型旋转
        self.displayImageView.layer.transform = CATransform3DMakeRotation(M_PI, 1.0, 1.0, 1.0);
        // 3d类型旋转
        self.displayImageView.layer.transform = CATransform3DMakeTranslation(100, 100, 100);
 // 第二种方式
        [self.displayImageView.layer setValue:[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.5, 1.8, 3.0)] forKeyPath:@"transform"];
        [self.displayImageView.layer setValue:[NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 1.0, 1.0, 1.0)] forKeyPath:@"transform"];
        [self.displayImageView.layer setValue:[NSValue valueWithCATransform3D:CATransform3DMakeTranslation(100, 100, 100)] forKeyPath:@"transform"];
  // 第三种方式 简单快速的实现 kvc 这种方式实现
        [self.displayImageView.layer setValue:@(1.5) forKeyPath:@"transform.scale"];
        [self.displayImageView.layer setValue:@(M_PI) forKeyPath:@"transform.rotation"];
        [self.displayImageView.layer setValue:@(150) forKeyPath:@"transform.translation.x"];

    }];
1.png
  • position 和anchorPosition的区别和联系
  • 解释anthorPosition 是图层的点默认在position的显示的位置,anchorPosition默认的是(0.5,0.5)范围是(0-1)表示图层的宽高比
  • 这两段代码是相同的
    CALayer *layer = [[CALayer alloc] init];
    layer.frame = CGRectMake(0, 0, 100, 100);
    layer.backgroundColor = [UIColor redColor].CGColor;
    [self.view.layer addSublayer:layer];
     CALayer *layer = [[CALayer alloc] init];
    layer.frame = CGRectMake(0, 0, 100, 100);
    layer.backgroundColor = [UIColor redColor].CGColor;
    layer.anchorPoint = CGPointMake(0.5, 0.5);
    [self.view.layer addSublayer:layer];
  • position 在0 和anchorPosition 在(0.5,0.5)
 CALayer *layer = [[CALayer alloc] init];
    layer.frame = CGRectMake(0, 0, 100, 100);
    layer.backgroundColor = [UIColor redColor].CGColor;
    layer.anchorPoint = CGPointMake(0.5, 0.5);
    layer.position = CGPointMake(0, 0);
    [self.view.layer addSublayer:layer];
1.png
  • position 在(30,50) 和anchorPosition 在(0,0)
 CALayer *layer = [[CALayer alloc] init];
    layer.frame = CGRectMake(0, 0, 100, 100);
    layer.backgroundColor = [UIColor redColor].CGColor;
    layer.anchorPoint = CGPointMake(0, 0);
    layer.position = CGPointMake(30,50);
    [self.view.layer addSublayer:layer];
1.png
  • 隐式动画
  • 自己的理解:他是系统非根层的动画,有我们自己创建的layer像改变position 、bounds、还有backgroundColor 等等的会存在默认的动画,原因是系统会创建一个包装的对象对他们进行包装,包装完毕才会执行改变
- (void)viewDidLoad {
    [super viewDidLoad];
    
    CALayer *layer = [[CALayer alloc] init];
    layer.frame = CGRectMake(0, 0, 100, 100);
    layer.backgroundColor = [UIColor redColor].CGColor;
    layer.anchorPoint = CGPointMake(0, 0);
    layer.position = CGPointMake(30,50);
    self.layer = layer;
    [self.view.layer addSublayer:layer];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    [CATransaction begin];
    // 取消动画
    [CATransaction setDisableActions:YES];
    // 设置动画的时常
    [CATransaction setAnimationDuration:5.0];
    self.layer.backgroundColor = [UIColor greenColor].CGColor;
    self.layer.position = CGPointMake(100, 200);
    [CATransaction commit];
    
}
  • 心跳动画 主要是CABasicAnimation的使用
  • 一些属性的说明(他的主要作用是 平移、旋转、缩放这些)
    duration 动画的时间
    repeatCount 重复的次数。不停重复设置为 HUGE_VALF
    repeatDuration 设置动画的时间。
    beginTime 指定动画开始的时间。从开始延迟几秒的话,设置为【CACurrentMediaTime() + 秒数】 的方式
    timingFunction 设置动画的速度变化
    autoreverses 动画结束时是否执行逆动画
    fromValue 所改变属性的起始值
    toValue 所改变属性的结束时的值
    byValue 所改变属性相同起始值的改变量
  • 心跳动画
 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    animation.duration = 1.5;
    animation.fromValue = @(0);
    animation.toValue = @(2);
    // 无限重复
    animation.repeatCount = NSIntegerMax;
    // 自动恢复原来的位置(有一个恢复的动画)
    animation.autoreverses = YES;
    // 确定完成后动画是否从目标图层的动画中移除。
    // 当“是”时,一旦活动持续时间过去,动画将从目标图层的动画中移除。 默认为YES
    animation.removedOnCompletion = NO;
    [self.heartimageView.layer addAnimation:animation forKey:nil];
  • 移动的动画
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    animation.duration = 2;
    animation.repeatCount = NSIntegerMax;
    animation.beginTime =CACurrentMediaTime() + 1;// 1秒后执行
    
    animation.fromValue = [NSValue valueWithCGPoint:self.heartimageView.layer.position]; // 起始帧
    
    animation.toValue = [NSValue valueWithCGPoint:CGPointMake(300, 300)]; // 终了帧
    
    // 视图添加动画
    
    [self.heartimageView.layer addAnimation:animation forKey:@"move-layer"];
  • 旋转的动画
 // 对Y轴进行旋转(指定Z轴的话,就和UIView的动画一样绕中心旋转)
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    animation.duration = 2;
    
    animation.repeatCount = NSIntegerMax;
    
    animation.beginTime = CACurrentMediaTime() + 1; // 1秒后执行
    
    animation.fromValue = [NSNumber numberWithFloat:0.0]; // 起始角度
    // 动画结束后停在最后位置状态的解决方法
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    
    animation.toValue = [NSNumber numberWithFloat:M_PI]; // 终止角度
    
    [self.heartimageView.layer addAnimation:animation forKey:@"rotate-layer"];
  • 透明度的动画
//opacity 指layer的透明度
    
    CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    
    basicAnimation.fromValue = @(1.0);
    
    basicAnimation.toValue  = @(0.0);//[NSNumber numberWithFloat:0.0]
    
    basicAnimation.duration = 1.5;
    
    [self.heartimageView.layer addAnimation:basicAnimation forKey:@"op"];
  • CAKeyframeAnimation 这个是线性的动画
  • 实现 一个图片绕着圆旋转
 CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 100, 100)];
    animation.path = path.CGPath;
    animation.duration = 3.0;
    animation.repeatCount = NSIntegerMax;
    // 自动恢复原位
    animation.autoreverses = YES;
    //  动画结束后停在最后位置状态的解决方法
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    // 线性起搏,可使动画在其持续时间内均匀发生
    // kCAMediaTimingFunctionEaseInEaseOut: 轻松放松起搏,可以使动画开始缓慢,在其持续时间的中间加速,然后在完成之前再次放慢速度。
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [self.heartimageView.layer addAnimation:animation forKey:nil];
  • 图片的抖动
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
    animation.values = @[@(angele(-10)),@(angele(0))];
    animation.duration = 1.0;
    animation.repeatCount = NSIntegerMax;
    // 自动恢复原位
    animation.autoreverses = YES;
    //  动画结束后停在最后位置状态的解决方法
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    // 线性起搏,可使动画在其持续时间内均匀发生
    // kCAMediaTimingFunctionEaseInEaseOut: 轻松放松起搏,可以使动画开始缓慢,在其持续时间的中间加速,然后在完成之前再次放慢速度。
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [self.heartimageView.layer addAnimation:animation forKey:nil];
  • 转场动画 (CATransition)
  • 图片的翻页效果
  index_ ++;
    if (index_ > 3 ) {
        index_ = 1;
    }
    NSString *imageName = [NSString stringWithFormat:@"%d",index_];
    self.heartimageView.image = [UIImage imageNamed:imageName];
    
    CATransition *animation = [CATransition animation];
    /*
     type:动画过渡类型
     subtype:动画过渡方向
     startProgress:动画起点(在整体动画的百分比)
     endProgress:动画终点(在整体动画的百分比)
     */
     // 动画的过度类型
   // animation.type = kCATransitionMoveIn;
    // 或者这个方式也行
    /*
     关于type的总结 后面的一张图片
     */
    animation.type = @"pageCurl";
     // 动画的过度方向
    animation.subtype = kCATransitionFromRight;
    animation.startProgress = 0.1;
    animation.endProgress = 0.7;
    [self.heartimageView.layer addAnimation:animation forKey:nil];
  • 关于type的总结


    1.jpg
  • CATransition的另外一种使用
    自带的动画
[UIView transitionWithView:self.heartimageView duration:2.0 options:UIViewAnimationOptionTransitionFlipFromBottom animations:^{
        index_ ++;
        if (index_ > 3 ) {
            index_ = 1;
        }
        NSString *imageName = [NSString stringWithFormat:@"%d",index_];
        self.heartimageView.image = [UIImage imageNamed:imageName];
    } completion:nil];
  • 自带动画2
//  前台页面
    UIView *frontView = [[UIView alloc] initWithFrame:self.view.bounds];
    frontView.backgroundColor = [UIColor colorWithRed:0.345 green:0.349 blue:0.365 alpha:1.000];
    UIImageView *caLogoView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"1"]];
    caLogoView.frame = CGRectMake(70, 80, caLogoView.bounds.size.width, caLogoView.bounds.size.height);
    [frontView addSubview:caLogoView];
    self.frontView = frontView;
    
    //  后台页面
    UIImage *backImage = [UIImage imageNamed:@"2"];
    UIImageView *backView = [[UIImageView alloc] initWithImage:backImage];
    backView.userInteractionEnabled = YES;
    self.backView = backView;
    
    [self.view addSubview:backView];
    [self.view addSubview:frontView];
 // formcview :在执行动画的过程中,他将会从superview 删除
    // toView :执行动画结束添加到fromview的父视图
    [UIView transitionFromView:self.frontView    // 从原来视图转到新视图的动画效果设置
                            toView:self.backView  duration:1.0f
                         options:UIViewAnimationOptionTransitionFlipFromLeft
                         completion:nil];
  • 动画组
 CAAnimationGroup *group = [CAAnimationGroup animation];
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    animation.fromValue = @(0);
    animation.toValue = @(1.5);
    
    CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation.fromValue = @(0);
    rotationAnimation.toValue = @(M_PI);
    group.animations = @[animation,rotationAnimation];
    group.duration = 1.5;
    group.repeatCount = NSIntegerMax;
    group.removedOnCompletion = NO;
    group.fillMode = kCAFillModeForwards;
    [self.heartimageView.layer addAnimation:group forKey:nil];
  • UIView的动画和图层动画的区别是:
    1.涂层动画不能处理点击等等的事件,本身的frame并不改变。
    2.UIView的动画可以出点点击事件等等 完成与用户的交互。
  • 一个图片折叠的动画
-(void)viewDidLoad {
    [super viewDidLoad];
    
    self.heartimageView.layer.contentsRect = CGRectMake(0, 0, 1, 0.5);
    self.bottomImageView.layer.contentsRect = CGRectMake(0, 0.5, 1, 0.5);
    
    self.heartimageView.layer.anchorPoint = CGPointMake(0.5, 1);
    self.bottomImageView.layer.anchorPoint = CGPointMake(0.5, 0);
    // 添加拖拽的手势
    UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
    [self.view addGestureRecognizer:panGes];
    // 添加隐影的操作(梯度的layer )
    CAGradientLayer *grandientLayer = [CAGradientLayer layer];
    grandientLayer.frame = self.bottomImageView.bounds;
    grandientLayer.colors = @[(id)[UIColor blackColor].CGColor,(id)[UIColor clearColor].CGColor];
    // 确定颜色的渐变方向
    grandientLayer.startPoint = CGPointMake(0, 0);
    grandientLayer.endPoint = CGPointMake(1.0, 1.0);
    // 一个颜色到下一个颜色开始渐变的位置
    grandientLayer.locations = @[@(0.2),@(0.8)];
    // 暗影
    grandientLayer.shadowOpacity = 0.0;
    // 不透明度
    grandientLayer.opacity = 0.0;
    self.grandientLayer = grandientLayer;
    [self.bottomImageView.layer addSublayer:grandientLayer];
}
/**
 添加拖拽的手势

 @param panGes 手势的点击
 */
- (void)panAction:(UIPanGestureRecognizer *)panGes {
    
    CGPoint point = [panGes locationInView:self.view];
    CGFloat angle =  M_PI * 1.0 * point.y /self.view.bounds.size.height;
    
    CATransform3D transform = CATransform3DIdentity;
    // 立体效果 离你眼睛越近 看的越清楚 离你眼睛越远 看的越小
    transform.m34 = -1.0/500.0;
    self.heartimageView.layer.transform = CATransform3DRotate(transform, -angle, 1.0, 0, 0);
    // 改变透明度
    self.grandientLayer.opacity = 1.0 * point.y /self.view.bounds.size.height;
    
    
    // 结束了进行反弹回去
    if (panGes.state == UIGestureRecognizerStateEnded) {
        // 一个动画
        // delay:动画开始时的等待时间,默认为0
        // usingSpringWithDamping: 反弹系数
        // initialSpringVelocity:动画的速度
        [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
            // 去掉所有的动画 回去
            self.heartimageView.layer.transform = CATransform3DIdentity;
            self.grandientLayer.opacity = 0.0;
        } completion:nil];
    }
}
1.png
  • 音乐震动条(类似于网易云音乐)
    音乐震动条
  • 利用 制作到影的动画
    CAReplicatorLayer *replicatorLayer = (CAReplicatorLayer *)self.view.layer;
    replicatorLayer.instanceCount = 2;
    //CAReplicatorLayer 旋转是按照本身的锚点来进行旋转的(这里是屏幕的正中央)
    replicatorLayer.instanceTransform = CATransform3DMakeRotation(M_PI, 1, 0, 0);   
    // 绘制阴影
    replicatorLayer.instanceRedOffset -= 0.1;
    replicatorLayer.instanceBlueOffset -= 0.1;
    replicatorLayer.instanceGreenOffset -= 0.1;
    // 透明度
    replicatorLayer.instanceAlphaOffset -= 0.1; 
2.png

1.png
  • 一个根据线条变化的动画
DrewView.h文件

/**
 开始的事件
 */
- (void)startAction;
/**
 重绘的方法
 */
- (void)redrewAction;

DrewView.m 文件
//
//  DrewView.m
//  基本动画的练习
//
//  Created by apple on 2018/3/19.
//  Copyright © 2018年 apple. All rights reserved.
//

#import "DrewView.h"

@interface DrewView ()

@property (strong, nonatomic) UIBezierPath *path;
@property (strong, nonatomic) CALayer *myLayer;
@property (strong, nonatomic) CAReplicatorLayer *replicatorLayer;


@end

@implementation DrewView

-(void)awakeFromNib {
    [super awakeFromNib];
    
    // 创建一个path
    UIBezierPath *path = [UIBezierPath bezierPath];
    self.path = path;
    // 创建手势
    UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
    [self addGestureRecognizer:panGes];
    
    // 创建一个layer
    CALayer *myLayer = [[CALayer alloc] init];
    myLayer.frame = CGRectMake(-10, 0, 10, 10);
    myLayer.cornerRadius = 5;
    myLayer.backgroundColor = [UIColor redColor].CGColor;
    self.myLayer = myLayer;
    // 创建复制层
    CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
    replicatorLayer.instanceDelay = 0.25;
    [replicatorLayer addSublayer:self.myLayer];
    [self.layer addSublayer:replicatorLayer];
    self.replicatorLayer = replicatorLayer;
    
}

/**
 拖拽的手势

 @param panGes 手势
 */
- (void)panAction:(UIPanGestureRecognizer *)panGes{
    
    CGPoint currentPoint = [panGes locationInView:self];
    if (panGes.state == UIGestureRecognizerStateBegan) {
      [self.path moveToPoint:currentPoint];
    }else if (panGes.state == UIGestureRecognizerStateChanged){
        [self.path addLineToPoint:currentPoint];
        [self setNeedsDisplay];
    }else if (panGes.state == UIGestureRecognizerStateEnded){
        [self setNeedsDisplay];
    }
}
/**
 开始绘制

 @param rect rect
 */
-(void)drawRect:(CGRect)rect {
    [self.path stroke];
    
}
/**
 开始的事件
 */
- (void)startAction{
    // 创建动画
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.path = self.path.CGPath;
    animation.duration = 1.0;
    animation.repeatCount = NSIntegerMax;
    // 自动复位
    animation.autoreverses = YES;
    // 消除停在最后的位置
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    // 动画匀速
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [self.myLayer addAnimation:animation forKey:nil];
    // 复制层
    self.replicatorLayer.instanceCount = 30;
//    self.replicatorLayer.instanceTransform = CATransform3DMakeTranslation(0, 2, 0);
    
}
/**
 重绘的方法
 */
- (void)redrewAction{
    [self.path removeAllPoints];
    [self.myLayer removeAllAnimations];
    [self setNeedsDisplay];
}
@end
IMG_0819.GIF
  • qq 粘性动画
bagdeButton.h文件
#import <UIKit/UIKit.h>

@interface bagdeButton : UIButton

@end
bagdeButton.m文件
#import "bagdeButton.h"


@interface bagdeButton ()

@property (strong, nonatomic) UIView *smallView;
@property (strong, nonatomic) CAShapeLayer *shapeLayer;


@end
@implementation bagdeButton

-(CAShapeLayer *)shapeLayer {
    if (!_shapeLayer) {
         // 创建根据路线显示涂层的层(形状图层)
        CAShapeLayer *shapeLayer = [CAShapeLayer layer];
        shapeLayer.fillColor = [UIColor redColor].CGColor;
        [self.superview.layer insertSublayer:shapeLayer atIndex:0];
        _shapeLayer = shapeLayer;
    }
    return _shapeLayer;
}


-(void)awakeFromNib {
    [super awakeFromNib];
    // 初始化
    [self setUp];
    
}
-(instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        // 初始化
        [self setUp];
    }
    return self;
}
/**
 初始化
 */
- (void)setUp{
    
    // 本身的基本的设置
    self.layer.cornerRadius = self.bounds.size.width *0.5;
    self.layer.masksToBounds = YES;
    self.titleLabel.textColor = [UIColor whiteColor];
    self.backgroundColor = [UIColor redColor];
    self.titleLabel.font = [UIFont systemFontOfSize:12.0];
    // 添加拖拽的手势
    UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
    [self addGestureRecognizer:panGes];
    // 添加小圆
    UIView *smallView = [[UIView alloc] initWithFrame:self.frame];
    smallView.backgroundColor = [UIColor redColor];
    smallView.layer.cornerRadius = self.bounds.size.width *0.5;
    self.smallView = smallView;
    [self.superview insertSubview:smallView belowSubview:self];
   
}
/**
 拖拽的手势

 @param panGes 手势
 */
- (void)panAction:(UIPanGestureRecognizer *)panGes {
    
    // 当前weiz的偏移量
    CGPoint panOff = [panGes locationInView:self.superview];
    CGRect frame = self.frame;
    frame.origin = panOff;
    self.frame = frame;
    
    
    // 计算两个圆直接的距离
    CGFloat dx1 = self.frame.origin.x - self.smallView.frame.origin.x;
    CGFloat dy1 = self.frame.origin.y - self.smallView.frame.origin.y;
    // 相当于求三角形的斜边
    CGFloat d = hypotf(dx1, dy1);
    // 改变小圆的frame
    CGRect smallRect = self.smallView.frame;
    CGFloat calculatorWidth = self.frame.size.width - self.frame.size.width *(1.0 * d/60);
    if (calculatorWidth <= 0) {
        calculatorWidth = 0;
    }
    smallRect.size.width = calculatorWidth;
    smallRect.size.height = calculatorWidth;
    self.smallView.frame = smallRect;
    self.smallView.layer.cornerRadius = calculatorWidth * 0.5;
    UIBezierPath *path = [self pathWithSmallView:self.smallView andBigView:self andRadius:d];
    self.shapeLayer.path = path.CGPath;
    
    if (panGes.state == UIGestureRecognizerStateEnded) {
        if (d < 60) {
            CGRect frame = self.frame;
            frame.origin = self.smallView.frame.origin;
            self.frame = frame;
            [self.shapeLayer removeFromSuperlayer];
            self.smallView.frame = self.frame;
            self.smallView.layer.cornerRadius = self.frame.size.width * 0.5;
            self.smallView.hidden = NO;
        }else {
            [self.shapeLayer removeFromSuperlayer];
            self.smallView.hidden = YES;
          
            // 创建一个imageview 来进行一个爆炸的效果
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, 100, 100)];
            NSMutableArray *imagesArray = [NSMutableArray array];
            for (NSInteger index = 1; index < 4; index ++) {
                NSString *imageName = [NSString stringWithFormat:@"%zd",index];
                UIImage *image = [UIImage imageNamed:imageName];
                [imagesArray addObject:image];
            }
            imageView.animationImages = imagesArray;
            [imageView startAnimating];
            [self.superview addSubview:imageView];
            // 移动从父试图
            [self removeFromSuperview];
            
        }
        
    }
}
/**
 根据小圆和大圆返回路径
 
 @param smallView 小圆
 @param bigButton 大圆
 @param d 半径
 @return 路径
 */
- (UIBezierPath *)pathWithSmallView:(UIView *)smallView andBigView:(UIButton *)bigButton andRadius:(CGFloat )d{
    
    // 求各个点的值
    CGFloat x1 = smallView.center.x;
    CGFloat y1 = smallView.center.y;
    CGFloat x2 = bigButton.center.x;
    CGFloat y2 = bigButton.center.y;
    CGFloat r1 = smallView.frame.size.width * 0.5;
    CGFloat r2 = bigButton.frame.size.width * 0.5;
    
    CGFloat siny = (x2 - x1)/d;
    CGFloat cosy = (y1 - y2)/d;
    CGPoint A = CGPointMake(x1 - cosy*r1, y1 - siny*r1);
    CGPoint B = CGPointMake(x1 + cosy*r1, y1 + siny*r1);
    CGPoint C = CGPointMake(x2 - cosy*r2, y2 - siny*r2);
    CGPoint D = CGPointMake(x2 + cosy*r2, y2 + siny*r2);
    CGPoint P = CGPointMake(A.x + siny*d/2.0, A.y - cosy*d/2.0);
    CGPoint O = CGPointMake(B.x + siny*d/2.0, B.y - cosy*d/2.0);
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:A];
    // 曲线
    [path addQuadCurveToPoint:C controlPoint:P];
    [path addLineToPoint:D];
    // 曲线
    [path addQuadCurveToPoint:B controlPoint:O];
    [path addLineToPoint:A];
    return path;
}
50B1870FAF9F924745754B4BE3836F2E.png
  • 微博的动画(类似与点击微博的➕)
WeiBoViewController.h文件
#import <UIKit/UIKit.h>

@interface WeiBoViewController : UIViewController

@end
 WeiBoViewController.m文件
#import "WeiBoViewController.h"

@interface WeiBoViewController ()

@property (strong, nonatomic) NSMutableArray *buttonArray;
@property (strong, nonatomic) NSTimer *timer;
@property (assign, nonatomic) int index;


@end
@implementation WeiBoViewController
-(NSMutableArray *)buttonArray {
    if (!_buttonArray) {
        _buttonArray = [NSMutableArray array];
    }
    return _buttonArray;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // 初始化
    [self setUp];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerChange) userInfo:nil repeats:YES];
}

/**
 定时器改变的方法
 */
- (void)timerChange{
    if (self.index == self.buttonArray.count) {
        [self.timer invalidate];
        self.timer = nil;
        return;
    }
    UIButton *button = self.buttonArray[self.index];
    // 这个动画有弹性效果
    [UIView animateWithDuration:0.2 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        // 恢复开始的样子
        button.transform = CGAffineTransformIdentity;
    } completion:nil];
    self.index ++;
}


/**
 初始化
 */
- (void)setUp{
    
    int row = 0;
    int column = 0;
    for (NSInteger index = 0; index < 6; index++) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        row = (int)index/3;
        column = (int)index%3;
        button.backgroundColor = [UIColor redColor];
        button.frame = CGRectMake(10 + column*(80 + 20), 467 + row*(80+20), 80, 80);
        [button setTitle:@"微博"forState:UIControlStateNormal];
        [self.view addSubview:button];
        [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchDown];
        [button addTarget:self action:@selector(buttonClickUpInside:) forControlEvents:UIControlEventTouchUpInside];
        // 移动到最底部
        button.transform = CGAffineTransformMakeTranslation(button.frame.origin.x, self.view.bounds.size.height);
        [self.buttonArray addObject:button];
    }
}

/**
 按钮按下事件

 @param button 按钮
 */
- (void)buttonClick:(UIButton *)button{
 
    button.transform = CGAffineTransformMakeScale(1.2, 1.2);
}
/**
 按钮按下抬起

 @param button 按钮
 */
- (void)buttonClickUpInside:(UIButton *)button {
    
    [UIView animateWithDuration:1.0 animations:^{
        button.transform = CGAffineTransformMakeScale(2, 2);
    }completion:^(BOOL finished) {
        button.alpha = 0;
    }];
}
@end
1.png
  • 例子效果
 // 创建粒子效果的layer
    CAEmitterLayer *emiterLayer = [[CAEmitterLayer alloc] init];
    // 创建发射器的位置
    emiterLayer.position = CGPointMake(self.view.bounds.size.width * 0.5, 100);
    // 开启三维的效果
    emiterLayer.preservesDepth = YES;
    // 创建每一个cell 并且给每一个cell添加属性
    CAEmitterCell *cell = [[CAEmitterCell alloc] init];
    // 设置粒子的速度
    cell.velocity = 100;
    cell.velocityRange = 50;
    // 例子的大小 scaleRange:(相当于scale 是 1.2 - 0.2)
    cell.scale = 0.7;
    cell.scaleRange = 0.5;
    // 粒子的方向
    cell.emissionLongitude = M_PI_2;
    cell.emissionRange = 0.5 * M_PI_2;
    // 设置每一秒弹出多少个
    cell.birthRate = 10;
    // 设置例子的旋转
    cell.spin = M_PI_2;
    cell.spinRange = M_PI_2;
    // 例子的存活时间
    cell.lifetime = 5;
    cell.lifetimeRange = 3;
    // 设置粒子的图片
    cell.contents = (id)[UIImage imageNamed:@"good6_30x30_"].CGImage;
    emiterLayer.emitterCells = @[cell];
    [self.view.layer addSublayer:emiterLayer];
image.png
  • 补充 CAShapeLayer
  • 作用 :根据路径显示图形
    链接
  • 终于总结完了 花了我两个星期的时间,如果大家感觉有什么不对的,千万给予指正,我将不胜荣幸!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容