MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C中的绘图技术Core Graphics入门

2022-02-144.9k 阅读

Core Graphics基础概述

Core Graphics简介

Core Graphics,也称为Quartz,是一个基于C语言的二维绘图框架,为在iOS和macOS应用中创建复杂的图形提供了强大的功能。它能够实现从简单的形状绘制到复杂的图像合成和特效处理。在Objective-C开发中,Core Graphics被广泛应用于自定义视图绘制、图形渲染以及图像编辑等场景。

Core Graphics提供了一套底层的API,这意味着开发者可以对绘图过程进行高度控制。无论是绘制线条、矩形、圆形等基本图形,还是处理渐变、阴影、透明度等图形效果,Core Graphics都能轻松胜任。与其他一些基于对象的绘图框架相比,Core Graphics基于过程式的编程风格,这使得开发者能够更直接地操作图形上下文,精确控制每一个绘图步骤。

图形上下文(Graphics Context)

在Core Graphics中,图形上下文是一个核心概念。它是一个包含了绘制状态信息的对象,例如当前的绘图颜色、线条宽度、变换矩阵等。所有的绘图操作都是在特定的图形上下文环境中进行的。当你想要进行绘图时,首先需要获取一个图形上下文。

在iOS中,最常见的获取图形上下文的方式是在视图的drawRect:方法中。当系统需要重绘视图时,会调用这个方法,并为其提供一个图形上下文。例如:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 在这里进行绘图操作
}

在上述代码中,UIGraphicsGetCurrentContext()函数获取了当前视图的图形上下文。在macOS中,获取图形上下文的方式稍有不同,通常是通过NSGraphicsContext类来获取。

图形上下文不仅仅是一个绘图的环境,它还管理着绘图的状态。比如,你可以设置线条的颜色、宽度,这些设置会影响后续在该上下文中绘制的所有线条。并且,图形上下文还支持状态的保存和恢复,通过CGContextSaveGState(context)CGContextRestoreGState(context)函数,你可以保存当前的绘图状态,进行一些临时的状态改变,然后再恢复到之前的状态,这在复杂的绘图操作中非常有用。

坐标系统

Core Graphics使用基于笛卡尔坐标系的坐标系统。在iOS中,坐标原点(0, 0)位于视图的左上角,x轴向右增长,y轴向下增长。而在macOS中,坐标原点同样在左上角,但y轴的方向与iOS相反,向上增长。理解坐标系统对于准确绘制图形至关重要。

当定义一个矩形或一个点的位置时,需要根据相应平台的坐标系统来确定其位置。例如,要在iOS视图中绘制一个位于(100, 100)位置,宽度为200,高度为150的矩形,可以这样做:

CGRect rect = CGRectMake(100, 100, 200, 150);

这里CGRectMake函数的第一个参数是矩形左上角的x坐标,第二个参数是y坐标,第三个参数是宽度,第四个参数是高度。在进行绘图时,要时刻注意坐标系统的方向,以确保图形绘制在预期的位置。

基本图形绘制

绘制线条

绘制线条是Core Graphics中最基本的操作之一。要绘制线条,需要以下几个步骤:首先,设置线条的属性,如颜色、宽度等;然后,定义线条的起点和终点;最后,告诉图形上下文绘制这条线。

下面是一个在iOS中绘制一条简单线条的示例:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 设置线条颜色为红色
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
    // 设置线条宽度为3.0
    CGContextSetLineWidth(context, 3.0);
    
    // 移动到起点(50, 50)
    CGContextMoveToPoint(context, 50, 50);
    // 绘制到终点(200, 150)
    CGContextAddLineToPoint(context, 200, 150);
    
    // 绘制线条
    CGContextStrokePath(context);
}

在上述代码中,CGContextSetStrokeColorWithColor函数设置了线条的颜色,CGContextSetLineWidth设置了线条宽度。CGContextMoveToPoint函数将绘图的起点移动到指定位置,CGContextAddLineToPoint函数从当前点绘制一条线到指定的终点。最后,CGContextStrokePath函数告诉图形上下文绘制这条路径(这里就是我们定义的线条)。

绘制矩形

绘制矩形也是常见的绘图操作。Core Graphics提供了多种绘制矩形的方式。一种简单的方法是使用CGContextFillRect函数来填充矩形,或者使用CGContextStrokeRect函数来绘制矩形的边框。

以下是填充和绘制矩形边框的示例代码:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 填充矩形
    CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
    CGRect fillRect = CGRectMake(100, 100, 150, 100);
    CGContextFillRect(context, fillRect);
    
    // 绘制矩形边框
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    CGRect strokeRect = CGRectMake(120, 120, 110, 60);
    CGContextStrokeRect(context, strokeRect);
}

在这段代码中,首先使用CGContextSetFillColorWithColor设置填充颜色,然后通过CGContextFillRect填充了一个绿色的矩形。接着,设置边框颜色和宽度后,使用CGContextStrokeRect绘制了一个蓝色边框的矩形。

绘制圆形和椭圆

绘制圆形和椭圆在Core Graphics中也很容易实现。由于圆形实际上是特殊的椭圆(长轴和短轴相等),我们可以使用CGContextAddEllipseInRect函数来绘制椭圆。如果矩形的宽度和高度相等,绘制出来的就是圆形。

以下是绘制圆形和椭圆的示例:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 绘制圆形
    CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
    CGRect circleRect = CGRectMake(150, 150, 80, 80);
    CGContextAddEllipseInRect(context, circleRect);
    CGContextFillPath(context);
    
    // 绘制椭圆
    CGContextSetStrokeColorWithColor(context, [UIColor purpleColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    CGRect ellipseRect = CGRectMake(250, 150, 120, 60);
    CGContextAddEllipseInRect(context, ellipseRect);
    CGContextStrokePath(context);
}

在上述代码中,首先绘制了一个黄色填充的圆形,通过定义一个正方形的矩形circleRect,并使用CGContextAddEllipseInRectCGContextFillPath来实现。然后,定义了一个矩形ellipseRect,其宽度和高度不相等,绘制出一个紫色边框的椭圆。

路径(Path)的使用

路径的概念

路径在Core Graphics中是一个重要的概念。它是由直线、曲线等几何形状组成的一个序列。路径可以是封闭的,如矩形、圆形,也可以是开放的,如线条。通过使用路径,你可以将多个图形组合在一起,进行复杂的图形绘制。

路径由一系列的点和连接这些点的线段组成。你可以通过CGContextMoveToPoint函数设置路径的起点,然后使用CGContextAddLineToPointCGContextAddArc等函数添加线段和曲线,从而构建出完整的路径。

创建复杂路径

创建复杂路径通常需要多个步骤。例如,要绘制一个类似云朵形状的路径,可以通过组合多个椭圆弧来实现。以下是一个示例:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 开始一个新路径
    CGContextBeginPath(context);
    
    // 移动到起点
    CGContextMoveToPoint(context, 100, 100);
    
    // 添加第一个椭圆弧
    CGContextAddArc(context, 120, 120, 30, 0, M_PI, 0);
    
    // 添加第二个椭圆弧
    CGContextAddArc(context, 160, 130, 40, M_PI, M_PI * 2, 0);
    
    // 封闭路径
    CGContextClosePath(context);
    
    // 设置填充颜色
    CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
    
    // 填充路径
    CGContextFillPath(context);
}

在上述代码中,首先使用CGContextBeginPath开始一个新路径,然后通过CGContextMoveToPoint设置起点。接着,使用CGContextAddArc添加两个椭圆弧,最后使用CGContextClosePath封闭路径。设置填充颜色后,通过CGContextFillPath填充路径,从而绘制出一个类似云朵的形状。

路径的填充和描边

路径创建完成后,可以对其进行填充和描边操作。填充操作使用CGContextFillPath函数,它会用当前设置的填充颜色填充路径所包围的区域。描边操作使用CGContextStrokePath函数,它会沿着路径的轮廓绘制线条,线条的颜色和宽度由当前上下文的设置决定。

例如,我们可以对前面绘制的云朵路径进行填充和描边:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 开始一个新路径
    CGContextBeginPath(context);
    
    // 移动到起点
    CGContextMoveToPoint(context, 100, 100);
    
    // 添加第一个椭圆弧
    CGContextAddArc(context, 120, 120, 30, 0, M_PI, 0);
    
    // 添加第二个椭圆弧
    CGContextAddArc(context, 160, 130, 40, M_PI, M_PI * 2, 0);
    
    // 封闭路径
    CGContextClosePath(context);
    
    // 设置填充颜色
    CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
    // 填充路径
    CGContextFillPath(context);
    
    // 设置描边颜色和宽度
    CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    // 描边路径
    CGContextStrokePath(context);
}

在这段代码中,先对路径进行了白色填充,然后设置灰色描边颜色和宽度为2.0,再对路径进行描边,使云朵形状既有填充又有边框。

颜色和透明度

颜色的设置

在Core Graphics中,设置颜色是通过CGContextSetFillColorWithColorCGContextSetStrokeColorWithColor等函数来实现的。这些函数接受一个CGColor对象作为参数,CGColor对象可以从UIColor(在iOS中)或NSColor(在macOS中)对象获取。

例如,在iOS中设置填充颜色为红色:

CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);

除了使用预定义的颜色,还可以通过[UIColor colorWithRed:green:blue:alpha:]方法创建自定义颜色。例如,创建一个半透明的绿色:

UIColor *customGreen = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:0.5];
CGContextSetFillColorWithColor(context, customGreen.CGColor);

透明度的控制

透明度在Core Graphics中通过颜色的alpha值来控制。alpha值的范围是0.0(完全透明)到1.0(完全不透明)。如上面创建半透明绿色的例子,alpha参数设置为0.5,使得颜色具有50%的透明度。

除了通过颜色的alpha值控制透明度,还可以使用CGContextSetAlpha函数来设置整个绘图操作的透明度。例如:

// 设置整体透明度为0.7
CGContextSetAlpha(context, 0.7);
// 绘制一个矩形
CGContextFillRect(context, CGRectMake(100, 100, 150, 100));

在上述代码中,在绘制矩形之前,通过CGContextSetAlpha将整体透明度设置为0.7,这样绘制出来的矩形就具有70%的透明度。无论是填充颜色还是描边颜色,都会受到这个透明度设置的影响。

渐变(Gradient)效果

线性渐变

线性渐变是一种常见的渐变效果,它沿着一条直线方向从一种颜色过渡到另一种颜色。在Core Graphics中创建线性渐变需要以下几个步骤:首先,定义渐变的颜色空间;然后,创建一个渐变对象;接着,定义渐变的起点和终点;最后,绘制渐变。

以下是一个在iOS中创建线性渐变的示例:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 定义渐变颜色
    NSArray *gradientColors = @[
        (id)[UIColor redColor].CGColor,
        (id)[UIColor yellowColor].CGColor
    ];
    CGFloat gradientLocations[] = {0.0, 1.0};
    
    // 创建渐变颜色空间
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    // 创建渐变对象
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)gradientColors, gradientLocations);
    
    // 定义渐变起点和终点
    CGPoint startPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
    
    // 绘制渐变
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    
    // 释放资源
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}

在上述代码中,首先定义了一个包含红色和黄色的渐变颜色数组gradientColors,以及对应的颜色位置数组gradientLocations。然后创建了设备RGB颜色空间colorSpace,并基于此颜色空间和渐变颜色创建了渐变对象gradient。定义了渐变的起点和终点后,使用CGContextDrawLinearGradient函数在视图中绘制线性渐变。最后,记得释放创建的渐变对象和颜色空间以避免内存泄漏。

径向渐变

径向渐变是从一个中心点向四周扩散的渐变效果。创建径向渐变的步骤与线性渐变类似,但需要定义两个圆,一个内圆和一个外圆,渐变从内圆的颜色过渡到外圆的颜色。

以下是创建径向渐变的示例代码:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 定义渐变颜色
    NSArray *gradientColors = @[
        (id)[UIColor blueColor].CGColor,
        (id)[UIColor whiteColor].CGColor
    ];
    CGFloat gradientLocations[] = {0.0, 1.0};
    
    // 创建渐变颜色空间
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    // 创建渐变对象
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)gradientColors, gradientLocations);
    
    // 定义内圆和外圆
    CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
    CGFloat radius1 = 50;
    CGFloat radius2 = 150;
    
    // 绘制径向渐变
    CGContextDrawRadialGradient(context, gradient, center, radius1, center, radius2, 0);
    
    // 释放资源
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}

在这段代码中,定义了蓝色到白色的渐变颜色。创建渐变对象后,通过CGContextDrawRadialGradient函数绘制径向渐变,该函数需要内圆和外圆的中心点、半径等参数。最后同样要释放相关资源。

阴影效果

添加阴影

在Core Graphics中添加阴影效果可以增强图形的立体感和层次感。通过CGContextSetShadowCGContextSetShadowWithColor函数可以为绘制的图形添加阴影。

以下是一个为矩形添加阴影的示例:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 设置阴影
    CGContextSetShadow(context, CGSizeMake(5, 5), 3.0);
    
    // 绘制矩形
    CGContextSetFillColorWithColor(context, [UIColor orangeColor].CGColor);
    CGRect rectToDraw = CGRectMake(100, 100, 150, 100);
    CGContextFillRect(context, rectToDraw);
}

在上述代码中,使用CGContextSetShadow函数设置了阴影,第一个参数CGSizeMake(5, 5)表示阴影在x和y方向上的偏移量,第二个参数3.0表示阴影的模糊半径。设置好阴影后,绘制了一个橙色的矩形,此时矩形就会带有阴影效果。

自定义阴影颜色

默认情况下,阴影的颜色是黑色。如果需要自定义阴影颜色,可以使用CGContextSetShadowWithColor函数。例如:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 自定义阴影颜色为灰色
    UIColor *shadowColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.8];
    CGContextSetShadowWithColor(context, CGSizeMake(3, 3), 2.0, shadowColor.CGColor);
    
    // 绘制圆形
    CGContextSetFillColorWithColor(context, [UIColor purpleColor].CGColor);
    CGRect circleRect = CGRectMake(150, 150, 80, 80);
    CGContextAddEllipseInRect(context, circleRect);
    CGContextFillPath(context);
}

在这段代码中,首先创建了一个灰色且带有一定透明度的阴影颜色shadowColor。然后使用CGContextSetShadowWithColor函数设置阴影,包括偏移量、模糊半径和自定义的阴影颜色。最后绘制一个紫色圆形,该圆形就会带有自定义颜色的阴影。

图形变换

平移变换

图形变换在Core Graphics中可以改变图形的位置、大小和方向。平移变换是将图形在坐标系中进行移动。通过CGContextTranslateCTM函数可以实现平移变换。

以下是一个将矩形进行平移的示例:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 保存当前图形上下文状态
    CGContextSaveGState(context);
    
    // 平移变换
    CGContextTranslateCTM(100, 100);
    
    // 绘制矩形
    CGContextSetFillColorWithColor(context, [UIColor brownColor].CGColor);
    CGRect rectToDraw = CGRectMake(0, 0, 150, 100);
    CGContextFillRect(context, rectToDraw);
    
    // 恢复图形上下文状态
    CGContextRestoreGState(context);
}

在上述代码中,首先使用CGContextSaveGState保存当前上下文状态,然后通过CGContextTranslateCTM(100, 100)将坐标系原点向右和向下分别移动100个单位。此时绘制的矩形(其左上角在新坐标系下为(0, 0))实际上在原坐标系下位于(100, 100)位置。最后使用CGContextRestoreGState恢复到原来的上下文状态,以避免对后续绘图操作产生影响。

旋转变换

旋转变换可以围绕一个点旋转图形。通过CGContextRotateCTM函数可以实现旋转变换,该函数接受一个弧度值作为参数,表示旋转的角度。

以下是一个将圆形进行旋转的示例:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 保存当前图形上下文状态
    CGContextSaveGState(context);
    
    // 移动到圆心位置
    CGContextTranslateCTM(CGRectGetMidX(rect), CGRectGetMidY(rect));
    
    // 旋转变换,旋转45度(M_PI_4弧度)
    CGContextRotateCTM(context, M_PI_4);
    
    // 绘制圆形
    CGContextSetFillColorWithColor(context, [UIColor cyanColor].CGColor);
    CGRect circleRect = CGRectMake(-40, -40, 80, 80);
    CGContextAddEllipseInRect(context, circleRect);
    CGContextFillPath(context);
    
    // 恢复图形上下文状态
    CGContextRestoreGState(context);
}

在这段代码中,先保存上下文状态,然后将坐标系原点平移到视图中心,接着使用CGContextRotateCTM函数旋转坐标系45度(M_PI_4弧度)。绘制圆形时,注意圆形的矩形定义是基于旋转后的坐标系。最后恢复上下文状态。

缩放变换

缩放变换可以改变图形的大小。通过CGContextScaleCTM函数可以实现缩放变换,该函数接受两个参数,分别表示在x和y方向上的缩放因子。

以下是一个对矩形进行缩放的示例:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 保存当前图形上下文状态
    CGContextSaveGState(context);
    
    // 缩放变换,x和y方向都缩放0.5倍
    CGContextScaleCTM(0.5, 0.5);
    
    // 绘制矩形
    CGContextSetFillColorWithColor(context, [UIColor magentaColor].CGColor);
    CGRect rectToDraw = CGRectMake(100, 100, 200, 150);
    CGContextFillRect(context, rectToDraw);
    
    // 恢复图形上下文状态
    CGContextRestoreGState(context);
}

在上述代码中,保存上下文状态后,通过CGContextScaleCTM(0.5, 0.5)将坐标系在x和y方向都缩放0.5倍。此时绘制的矩形看起来大小是原矩形的一半。最后恢复上下文状态。

通过以上对Core Graphics的入门介绍,你可以在Objective-C开发中利用这些绘图技术创建出丰富多样的图形界面和特效。在实际应用中,还可以结合其他iOS或macOS框架,进一步拓展图形绘制的功能和应用场景。