Core Graphics系列学习(二)

2,195 阅读5分钟

前言

上一篇文章简单的介绍了Core Graphics的概念,这篇文章来介绍一下Core Graphics在实际开发中的使用

图形绘制

线段

- (void)drawRect:(CGRect)rect {
	// 获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 设置线宽
    CGContextSetLineWidth(ctx, 2);
    // 设置起点
    CGContextMoveToPoint(ctx, 10, 10);
    // 第二个点
    CGContextAddLineToPoint(ctx, 250, 250);
    // 设置颜色
    [[UIColor redColor]set];
    // 渲染
    CGContextStrokePath(ctx);
}

矩形

- (void)drawRect:(CGRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 线宽
    CGContextSetLineWidth(ctx, 2);
    // 矩形
    CGContextAddRect(ctx, CGRectMake(0, 0, 200, 200));
    // 设置颜色
    [[UIColor redColor]set];
    // 渲染
    CGContextStrokePath(ctx);
}

- (void)drawRect:(CGRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 线宽
    CGContextSetLineWidth(ctx, 2);
    /*
     参数1:目标上下文
     参数2:圆心x
     参数3:圆心y
     参数4:半径
     参数5:开始弧度
     参数6:结束弧度
     参数7:绘制方向,NO:顺时针; YES:逆时针
     */
    // 圆
    CGContextAddArc(ctx, 40, 200, 40, 0, 2*M_PI, YES);
    // 设置颜色
    [[UIColor redColor]set];
    // 渲染
    CGContextStrokePath(ctx);
}

贝塞尔曲线

二阶贝塞尔

- (void)drawRect:(CGRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 线宽
    CGContextSetLineWidth(ctx, 2);
    // 虚线
    CGContextSetLineDash(ctx, 0, (CGFloat[]){10,5}, 2);
    // 起点
    CGContextMoveToPoint(ctx, 100, 100);
    /*
     参数1:目标上下文
     参数2:控制点 x坐标
     参数3:控制点 y坐标
     参数4:结束点 x坐标
     参数5:结束点 y坐标
     */
    // 二阶贝塞尔
    CGContextAddQuadCurveToPoint(ctx, 300, 300, 100, 500);
    // 设置颜色
    [[UIColor redColor]set];
    // 渲染
    CGContextStrokePath(ctx);
    
}

三阶贝塞尔

- (void)drawRect:(CGRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 线宽
    CGContextSetLineWidth(ctx, 2);
    /*
     参数1:目标上下文
     参数2:控制点1 x坐标
     参数3:控制点1 y坐标
     参数4:控制点2 x坐标
     参数5:控制点2 y坐标
     参数6:结束点 x坐标
     参数7:结束点 y坐标
     */
    // 三阶贝塞尔曲线
    CGContextAddCurveToPoint(ctx, 40, 200, 300, 300, 100, 500);
    // 设置颜色
    [[UIColor redColor]set];
    // 渲染
    CGContextStrokePath(ctx);
}

图片处理

图片处理不再是在drawRect:方法处理,而是调用CGBitmapContextCreate()去创建一个图片的上下文,通过处理这个上下文达到我们想要的效果后输出UIImage对象

灰度图

- (UIImage *)grayImage:(UIImage *)image {
    // 创建灰度色彩空间的对象
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
    /*
     参数1:指向要渲染的绘制内存的地址
     参数2:高度
     参数3:宽度
     参数4:表示内存中像素的每个组件的位数
     参数5:每一行在内存所占的比特数
     参数6:表示上下文使用的颜色空间
     参数7:表示是否包含透明通道
     */
    // 创建一个图片上下文
    CGContextRef context = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, 0, colorSpace, kCGImageAlphaNone);
    // 释放色彩空间的对象
    CGColorSpaceRelease(colorSpace);
    if (!context) {
        return nil;
    }
    // 绘制
    CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
    // 创建UIImage对象
    UIImage *grayImage = [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)];
    // 释放上下文
    CGContextRelease(context);
    return grayImage;
}

二值化

- (UIImage *)binarizationImage:(UIImage *)image {
    int width = image.size.width;
    int height = image.size.height;
    // 创建一个等于图片大小像素的数组
    uint32_t *pixels = (uint32_t *)malloc(width * height * sizeof(uint32_t));
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    // 数组清零
    memset(pixels, 0, width*height * sizeof(uint32_t));
    /*
     参数1:指向要渲染的绘制内存的地址
     参数2:高度
     参数3:宽度
     参数4:表示内存中像素的每个组件的位数
     参数5:每一行在内存所占的比特数
     参数6:表示上下文使用的颜色空间
     参数7:表示是否包含透明通道
     */
    CGContextRef context = CGBitmapContextCreate(pixels, width, height, 8, width*sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.CGImage);
    
    int tt =1;
    CGFloat intensity;
    int bw;
    //
    for (int y = 0; y <height; y++) {
        for (int x =0; x <width; x ++) {
            uint8_t *rgbaPixel = (uint8_t *)&pixels[y*width+x];
            intensity = (rgbaPixel[tt] + rgbaPixel[tt + 1] + rgbaPixel[tt + 2]) / 3. / 255.;
            
            bw = intensity > 0.45?255:0;
            
            rgbaPixel[tt] = bw;
            rgbaPixel[tt + 1] = bw;
            rgbaPixel[tt + 2] = bw;
            
        }
    }
    // 通过上下文 创建一个CGImageRef
    CGImageRef imageRef = CGBitmapContextCreateImage(context);
    // 上下文释放
    CGContextRelease(context);
    // 颜色对象释放
    CGColorSpaceRelease(colorSpace);
    // 释放数组
    free(pixels);
    // 通过CGImage创建UIImage
    UIImage *resImage = [UIImage imageWithCGImage:imageRef];
    // 释放CGImage
    CGImageRelease(imageRef);
    return resImage;
}

图片分割

- (UIImage *)CutImageWithImage:(UIImage *)image withRect:(CGRect)rect
{
    /*
     参数1:CGImage类型
     参数2:要切割的CGRect
     */
    CGImageRef cutImage = CGImageCreateWithImageInRect(image.CGImage, rect);
    // 将切割出得图片转换为UIImage
    UIImage *result = [UIImage imageWithCGImage:cutImage];
    return result;
}

图片拼接

- (UIImage *)spliceImage:(UIImage *)image1 toImage:(UIImage *)image2 {
    CGSize size = CGSizeMake(image1.size.width, image1.size.height);
    // 创建一个基于位图的上下文(context),并将其设置为当前上下文(context)
    UIGraphicsBeginImageContext(size);
        
    // Draw image2
    [image2 drawInRect:CGRectMake(4.5, 6, 77, 77)];
        
    // Draw image1
    [image1 drawInRect:CGRectMake(0, 0, image1.size.width, image1.size.height)];
    // 获取拼接好的图片
    UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭图形上下文
    UIGraphicsEndImageContext();
        
    return resultingImage;
}

页面截图

- (UIImage *)captureView:(UIImageView *)view {
    /*
     参数1:绘制的尺寸大小
     参数2:透明开关,如果图形完全不用透明,设置为YES以优化位图的存储。
     参数3:设为0后,系统就会自动设置正确的比例
     */
    // 创建一个基于位图的上下文(context),并将其设置为当前上下文(context)。
    UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
    // 渲染view.layer到当前context
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    // 通过context获取UIImage
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭图形上下文
    UIGraphicsEndImageContext();
    return image;
}

实战

分页控制器自定义形状

项目中要自定义一个不规则的tabar,如图。上网找到的方法都是iOS 13以下才能用,iOS 13以上就失效。分析一下这个需求,是往下凹而不是往上凸,所以目前这个办法是可以解决这个需求的。
思路:
1.先自定义一个view,然后绘制我们需要的形状,通过mask属性去设置遮罩。
2.完成后截图生成一张图片,把这种图片设置成tabar的背景图

// .h
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface BaseTabBar : UITabBar

@end

NS_ASSUME_NONNULL_END

// .m
- (void)setCurveBG {
    UIView *back = [UIView new];{
        back.backgroundColor = YELLOW_COLOR;
        [self setShadowImage:[UIImage new]];
        back.frame = self.bounds;
        // 绘制中间的曲线部分
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(ViewWidth(back) /5, 0.f)];
        [path addCurveToPoint:CGPointMake(ViewWidth(back)/2, 35) controlPoint1:CGPointMake(ViewWidth(back) *3/7, 0) controlPoint2:CGPointMake(ViewWidth(back)/2-35, 35)];
        [path addCurveToPoint:CGPointMake(ViewWidth(back) *4/5, 0.f) controlPoint1:CGPointMake(ViewWidth(back)/2+35, 35) controlPoint2:CGPointMake(ViewWidth(back) *4/7, 0)];
        [path closePath];
        
        // 矩形部分
        UIBezierPath *path2 = [UIBezierPath bezierPath];
        [path2 moveToPoint:CGPointMake(0.f, 0.f)];
        [path2 addLineToPoint:CGPointMake(ViewWidth(back), 0.f)];
        [path2 addLineToPoint:CGPointMake(ViewWidth(back), ViewHeight(back))];
        [path2 addLineToPoint:CGPointMake(0.f, ViewHeight(back))];
        [path2 closePath];
        
        // 将曲线部分路径添加到矩形部分
        [path2 appendPath:path];
        // 设置遮罩路径
        CAShapeLayer *shapLayer = [CAShapeLayer layer];
        shapLayer.path = path2.CGPath;
        back.layer.mask = shapLayer;
        self.translucent = YES;
        // 截图
        UIImage *image = [self convertViewToImage:back];
        self.backgroundImage = image;
    }
}

参考文章

【IOS】图片二值化和黑白(灰度)处理