23.Quartz2D总结

    xiaoxiao2026-03-20  9

    一.Quartz2D简介

    Quartz2D是二维的绘图引擎(经包装的函数库,方便开发者使用。也就是说苹果帮我们封装了一套绘图的函数库),用Quartz2D写的同一份代码,既可以运行在iphone上又可以运行在mac上,可以跨平台开发,开发中比较常用的是截屏/裁剪/自定义UI控件,Quartz2D在iOS开发中的价值就是自定义UI控件.

    二.绘图简介

    1.绘图的步骤: 1.获取上下文 CG:表示这个类在CoreGraphics框架里 Ref:引用,目前学的上下文都跟UIGraphics有关,想获取图形上下文,首先敲UIGraphics。 2.创建路径(描述路径) 一般开发中用贝塞尔路径,里面封装了很多东西,可以帮我画一些基本的线段,矩形,圆等等 4.渲染上下文 首先获取图形上下文,然后描述路径,把路径添加到上下文,渲染到视图,图形上下文相当于一个内存缓存区,在内存里面操作是最快的,比直接在界面操作快多了 3.当这个View要显示的时候才会调用drawRect绘制图形,viewDidLoad后才会调用,因为那时候还没显示视图 4.注意:drawRect不能手动调用,因为图形上下文我们自己创建不了,只能由系统帮我们创建,并且传递给我们.需要我们手动调用 [self setNeedsDisplay](重绘,系统会先创建与view相关联的上下文,然后再调用drawRect)

    三.Quartz2D绘图演练

    1.绘制直线

    最原始的绘图方式 // 1.获取图形上下文 // 目前我们所用的上下文都是以UIGraphics // CGContextRef Ref:引用 CG:目前使用到的类型和函数 一般都是CG开头 CoreGraphics CGContextRef ctx = UIGraphicsGetCurrentContext(); // 2.描述路径 // 创建路径 CGMutablePathRef path = CGPathCreateMutable(); // 设置起点 // path:给哪个路径设置起点 CGPathMoveToPoint(path, NULL, 50, 50); // 添加一根线到某个点 CGPathAddLineToPoint(path, NULL, 200, 200); // 3.把路径添加到上下文 CGContextAddPath(ctx, path); // 4.渲染上下文 CGContextStrokePath(ctx);

    绘直线第二种方式

    // 获取上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); // 描述路径 // 设置起点 CGContextMoveToPoint(ctx, 50, 50); CGContextAddLineToPoint(ctx, 200, 200); // 渲染上下文 CGContextStrokePath(ctx);

    绘直线第三种方式

    // UIKit已经封装了一些绘图的功能 // 贝瑟尔路径 // 创建路径 UIBezierPath *path = [UIBezierPath bezierPath]; // 设置起点 [path moveToPoint:CGPointMake(50, 50)]; // 添加一根线到某个点 [path addLineToPoint:CGPointMake(200, 200)]; // 绘制路径 [path stroke];

    2.绘制曲线

    最原始的绘图方式 // 原生绘制方法 // 获取上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); // 描述路径 // 设置起点 CGContextMoveToPoint(ctx, 50, 50); // cpx:控制点的x CGContextAddQuadCurveToPoint(ctx, 150, 20, 250, 50); // 渲染上下文 CGContextStrokePath(ctx);

    3.绘制图形

    圆弧 // Center:圆心 // startAngle:弧度 // clockwise:YES:顺时针 NO:逆时针 // 扇形 CGPoint center = CGPointMake(125, 125); UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_2 clockwise:YES]; // 添加一根线到圆心 [path addLineToPoint:center]; // 封闭路径,关闭路径:从路径的终点到起点 [path closePath]; [path stroke]; // 填充:必须是一个完整的封闭路径,默认就会自动关闭路径 // [path fill]; 饼状图 //随机绘制饼状图 - (NSArray *)arrRandom { int totoal = 100; NSMutableArray *arrM = [NSMutableArray array]; int temp = 0; for (int i = 0; i < arc4random_uniform(10) + 1; i++) { temp = arc4random_uniform(totoal) + 1; // 100 1~100 // 随机出来的临时值等于总值,直接退出循环,因为已经把总数分配完毕,没必要在分配。 [arrM addObject:@(temp)]; // 解决方式:当随机出来的数等于总数直接退出循环。 if (temp == totoal) { break; } totoal -= temp; } //如果余下的值不为0的情况 if (totoal) { [arrM addObject:@(totoal)]; } return arrM; } - (void)drawRect:(CGRect)rect { NSArray *arr = [self arrRandom]; CGFloat radius = rect.size.width * 0.5; CGPoint center = CGPointMake(radius, radius); CGFloat startA = 0; CGFloat angle = 0; CGFloat endA = 0; for (int i = 0; i < arr.count; i++) { startA = endA; angle = [arr[i] integerValue] / 100.0 * M_PI * 2; endA = startA + angle; UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES]; [path addLineToPoint:center]; [[self colorRandom] set]; [path fill]; } } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //重新绘制 [self setNeedsDisplay]; } - (UIColor *)colorRandom { // 0 ~ 255 / 255 // OC:0 ~ 1 CGFloat r = arc4random_uniform(256) / 255.0; CGFloat g = arc4random_uniform(256) / 255.0; CGFloat b = arc4random_uniform(256) / 255.0; return [UIColor colorWithRed:r green:g blue:b alpha:1]; } 柱状图 - (void)drawRect:(CGRect)rect { NSArray *arr = [self arrRandom]; CGFloat x = 0; CGFloat y = 0; CGFloat w = 0; CGFloat h = 0; for (int i = 0 ; i < arr.count; i++) { w = rect.size.width / (2*arr.count - 1); x = 2 * w * i; h = [arr[i] floatValue] / 100.0 *rect.size.height; y = rect.size.height - h; UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, w, h)]; [[self colorRandom] set]; [path fill]; } } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self setNeedsDisplay]; } - (NSArray *)arrRandom { int total = 100; NSMutableArray *arrM = [NSMutableArray array]; int temp = 0; for (int i = 0 ; i < arc4random_uniform(10) + 1; i++) { temp = arc4random_uniform(total) + 1; [arrM addObject:@(temp)]; if (temp == total) { break; } total -= temp; } if (total) { [arrM addObject:@(total)]; } return arrM; } - (UIColor *)colorRandom { // 0 ~ 255 / 255 // OC:0 ~ 1 CGFloat r = arc4random_uniform(256) / 255.0; CGFloat g = arc4random_uniform(256) / 255.0; CGFloat b = arc4random_uniform(256) / 255.0; return [UIColor colorWithRed:r green:g blue:b alpha:1]; } 绘制文字 // 绘制文字 NSString *str = @"asfdsfsdf"; // 文字的起点 // Attributes:文本属性 NSMutableDictionary *textDict = [NSMutableDictionary dictionary]; // 设置文字颜色 textDict[NSForegroundColorAttributeName] = [UIColor redColor]; // 设置文字字体 textDict[NSFontAttributeName] = [UIFont systemFontOfSize:30]; // 设置文字的空心颜色和宽度 textDict[NSStrokeWidthAttributeName] = @3; textDict[NSStrokeColorAttributeName] = [UIColor yellowColor]; // 创建阴影对象 NSShadow *shadow = [[NSShadow alloc] init]; shadow.shadowColor = [UIColor greenColor]; shadow.shadowOffset = CGSizeMake(4, 4); shadow.shadowBlurRadius = 3; textDict[NSShadowAttributeName] = shadow; // 富文本:给普通的文字添加颜色,字体大小 [str drawAtPoint:CGPointZero withAttributes:textDict]; //不会换行 绘制图片 // 超出裁剪区域的内容全部裁剪掉 // 注意:裁剪必须放在绘制之前 UIRectClip(CGRectMake(0, 0, 50, 50));//裁剪图片 UIImage *image = [UIImage imageNamed:@"001"]; // 默认绘制的内容尺寸跟图片尺寸一样大 [image drawAtPoint:CGPointZero]; //绘制图片在一定区域内 //[image drawInRect:rect]; // 绘图(平铺图片) //[image drawAsPatternInRect:rect];

    4.绘图中的定时器

    如果在绘图的时候需要用到定时器,通常使用CADisplayLink

    //CADisplayLink:每次屏幕刷新的时候就会调用,屏幕一般一秒刷新60次 CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeChange)]; // 添加主运行循环 [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; - (void)timeChange { //注意:这个方法并不会马上调用drawRect,其实这个方法只是给当前控件添加刷新的标记,等下一次屏幕刷新的时候才会调用drawRect [self setNeedsDisplay]; }

    5.矩阵操作(平移,缩放,旋转)

    - (void)drawRect:(CGRect)rect { // 1.获取上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); // 2.描述路径 UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-100, -50, 200, 100)]; [[UIColor redColor] set]; // 上下文矩阵操作 // 注意:矩阵操作必须要在添加路径之前 // 平移 CGContextTranslateCTM(ctx, 100, 50); // 缩放 CGContextScaleCTM(ctx, 0.5, 0.5); // 旋转 CGContextRotateCTM(ctx, M_PI_4); // 3.把路径添加上下文 CGContextAddPath(ctx, path.CGPath); [[UIColor redColor] set]; // 4.渲染上下文 CGContextFillPath(ctx); }

    四.Quartz2D应用

    1.图片水印

    // 加载图片 UIImage *image = [UIImage imageNamed:@"小黄人"]; // 0.获取上下文,之前的上下文都是在view的drawRect方法中获取(跟View相关联的上下文layer上下文) // 目前我们需要绘制图片到新的图片上,因此需要用到位图上下文 // 怎么获取位图上下文,注意位图上下文的获取方式跟layer上下文不一样。位图上下文需要我们手动创建。 // 开启一个位图上下文,注意位图上下文跟view无关联,所以不需要在drawRect. // size:位图上下文的尺寸(新图片的尺寸) // opaque: 不透明度 YES:不透明 NO:透明,通常我们一般都弄透明的上下文 // scale:通常不需要缩放上下文,取值为0,表示不缩放 UIGraphicsBeginImageContextWithOptions(image.size, NO, 0); UIBezierPath *path =[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 300, 300)]; [[UIColor redColor] set]; [path stroke]; // 1.绘制原生的图片 [image drawAtPoint:CGPointZero]; // 2.给原生的图片添加文字 NSString *str = @"小码哥"; // 创建字典属性 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[NSForegroundColorAttributeName] = [UIColor redColor]; dict[NSFontAttributeName] = [UIFont systemFontOfSize:20]; [str drawAtPoint:CGPointMake(200, 528) withAttributes:dict]; // 3.生成一张图片给我们,从上下文中获取图片 UIImage *imageWater = UIGraphicsGetImageFromCurrentImageContext(); // 4.关闭上下文 // UIGraphicsEndImageContext(); self.imageView.image = imageWater;

    2.图片裁剪

    + (UIImage *)imageWithClipImage:(UIImage *)image borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)color { // 图片的宽度和高度 CGFloat imageWH = image.size.width; // 设置圆环的宽度 CGFloat border = borderWidth; // 圆形的宽度和高度 CGFloat ovalWH = imageWH + 2 * border; // 1.开启上下文 UIGraphicsBeginImageContextWithOptions(CGSizeMake(ovalWH, ovalWH), NO, 0); // 2.画大圆 UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, ovalWH, ovalWH)]; [color set]; [path fill]; // 3.设置裁剪区域 UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(border, border, imageWH, imageWH)]; [clipPath addClip]; // 4.绘制图片 [image drawAtPoint:CGPointMake(border, border)]; // 5.获取图片 UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext(); // 6.关闭上下文 UIGraphicsEndImageContext(); return clipImage; }

    3.图片截取

    //根据手指移动截取任意大小的图片 //1.记录开始节点 //2.添加指定区域截取图片 //3.裁剪图片 // 给控制器的view添加一个pan手势 //记录开始节点 @property (nonatomic, assign) CGPoint startP; @property (weak, nonatomic) UIImageView *imageV; @property (nonatomic, weak) UIView *clipView; //设置裁剪显示的View - (UIView *)clipView{ if (_clipView == nil) { UIView *view = [[UIView alloc] init]; _clipView = view; view.backgroundColor = [UIColor blackColor]; view.alpha = 0.5; [self.view addSubview:view]; } return _clipView; } UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; [self.view addGestureRecognizer:pan]; //裁剪 - (void)pan:(UIPanGestureRecognizer *)pan { CGPoint endA = CGPointZero; if (pan.state == UIGestureRecognizerStateBegan) { // 一开始拖动的时候 // 获取一开始触摸点 _startP = [pan locationInView:self.view]; }else if(pan.state == UIGestureRecognizerStateChanged){ // 一直拖动 // 获取结束点 endA = [pan locationInView:self.view]; CGFloat w = endA.x - _startP.x; CGFloat h = endA.y - _startP.y; // 获取截取范围 CGRect clipRect = CGRectMake(_startP.x, _startP.y, w, h); // 生成截屏的view self.clipView.frame = clipRect; } else if (pan.state == UIGestureRecognizerStateEnded){ // 图片裁剪,生成一张新的图片 // 开启上下文 // 如果不透明,默认超出裁剪区域会变成黑色,通常都是透明 UIGraphicsBeginImageContextWithOptions(_imageV.bounds.size, NO, 0); // 设置裁剪区域 UIBezierPath *path = [UIBezierPath bezierPathWithRect:_clipView.frame]; [path addClip]; // 获取上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); // 把控件上的内容渲染到上下文 [_imageV.layer renderInContext:ctx]; // 生成一张新的图片 _imageV.image = UIGraphicsGetImageFromCurrentImageContext(); // 关闭上下文 UIGraphicsEndImageContext(); // 先移除 [_clipView removeFromSuperview]; // 截取的view设置为nil _clipView = nil; } }

    4.屏幕截屏

    //1.开启位图上下文 UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0); //2.获取上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); //3.把控件上的图层渲染到上下文,layer只能渲染 [view.layer renderInContext:ctx]; //4.生成一张图片 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); //5.关闭上下文 UIGraphicsEndImageContext();

    5.图片擦除

    //在拖动手势中做图片擦除 UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; [self.view addGestureRecognizer:pan]; - (void)pan:(UIPanGestureRecognizer *)pan { //1.获取当前点 CGPoint curP = [pan locationInView:self.view]; //2.获取擦除的矩形范围 CGFloat wh = 100; CGFloat x = curP.x - wh * 0.5; CGFloat y = curP.y - wh * 0.5; CGRect rect = CGRectMake(x, y, wh, wh); //3.开启上下文 UIGraphicsBeginImageContextWithOptions(self.view .bounds.size, NO, 0); //4.获取上下文 CGContextRef ctx=UIGraphicsGetCurrentContext(); //5.把控件的layer渲染上去 [_imageView.layer renderInContext:ctx]; //6.擦除图片 CGContextClearRect(ctx, rect); //7.生成一张图片 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); _imageView.image = image; //8.关闭上下文 UIGraphicsEndImageContext(); }
    转载请注明原文地址: https://ju.6miu.com/read-1308166.html
    最新回复(0)