50、利用 Core Graphics 进行绘图

利用 Core Graphics 进行绘图

1. Core Graphics 与 Quartz 2D 简介

在开发应用时,UIKit 框架中的视图和控件能满足很多需求,但有些视觉元素仅靠 UIKit 预定义的组件无法完全实现,这时就需要 Core Graphics 框架。Core Graphics 可完成多种绘图任务,其中一个主要组件是 Quartz 2D。

Quartz 2D 是一组函数、数据类型和对象的集合,能让我们直接在视图或内存中的图像上绘图。它将绘图的视图或图像视为虚拟画布,遵循画家模型,即绘图命令的应用方式和在画布上涂颜料类似。例如,先将整个画布涂成红色,再将下半部分涂成蓝色,根据蓝色颜料的透明度,画布会呈现出一半红色一半蓝色或紫色的效果。Quartz 2D 提供了多种绘制线条、形状和图像的函数,但仅限于二维绘图。

2. Quartz 2D 的绘图方式

使用 Quartz 2D 时,通常将绘图代码添加到进行绘图的视图中。比如创建 UIView 的子类,并在该类的 drawRect: 方法中添加 Quartz 函数调用。 drawRect: 方法是 UIView 类定义的一部分,每次视图需要重绘时都会被调用。

3. Quartz 2D 的图形上下文

在 Quartz 以及整个 Core Graphics 中,绘图在图形上下文中进行,通常简称为上下文。每个视图都有一个关联的上下文,可通过以下代码获取当前上下文:

CGContextRef context = UIGraphicsGetCurrentContext();

注:Core Graphics 是 C 语言 API,代码示例中会有很多 C 语法。

定义图形上下文后,可将其传递给各种 Core Graphics 绘图函数进行绘图。例如,以下代码将创建一条简单的线并绘制出来:

CGContextSetLineWidth(context, 4.0);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextMoveToPoint(context, 10.0, 10.0);
CGContextAddLineToPoint(context, 20.0, 20.0);
CGContextStrokePath(context);

上述代码中,各步骤的含义如下:
1. CGContextSetLineWidth(context, 4.0); :指定后续创建当前路径的绘图命令使用 4 点宽的画笔。
2. CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor); :指定描边颜色为红色。在 Core Graphics 中,绘图操作关联两种颜色:
- 描边颜色:用于绘制线条和形状的轮廓。
- 填充颜色:用于填充形状。
3. CGContextMoveToPoint(context, 10.0, 10.0); :将虚拟笔移动到指定位置 (10, 10),不进行实际绘图。
4. CGContextAddLineToPoint(context, 20.0, 20.0); :从当前笔的位置 (10, 10) 绘制一条线到指定位置 (20, 20),该位置成为新的笔位置。
5. CGContextStrokePath(context); :告诉 Quartz 绘制已构建的路径,使用之前设置的线宽和描边颜色使路径可见。

4. 坐标系统

在 Core Graphics 坐标系统中,位置由 x 和 y 坐标表示,通常写成 (x, y)。上下文的左上角是 (0, 0),向下移动时 y 值增加,向右移动时 x 值增加。这与许多图形库和传统笛卡尔坐标系不同,在其他系统中,(0, 0) 通常在左下角,y 值增加表示向上移动。

为指定坐标系统中的点,一些 Quartz 函数需要两个浮点数作为参数,另一些函数则要求将点嵌入到 CGPoint 结构体中。描述视图或其他对象的大小时,Quartz 使用 CGSize 结构体,定义矩形时使用 CGRect 结构体,它包含一个 CGPoint 类型的 origin 表示矩形的左上角,以及一个 CGSize 类型的 size 表示矩形的宽度和高度。

5. 指定颜色

绘图中颜色很重要,在 iOS 上,UIKit 提供了 UIColor 类来表示颜色,但不能直接在 Core Graphic 调用中使用 UIColor 对象。 UIColor CGColor 的包装类,可通过其 CGColor 属性获取 CGColor 引用,例如:

CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
6. 颜色理论

在现代计算机图形中,屏幕上显示的任何颜色的数据都基于某种颜色模型存储。常见的颜色表示方法是使用红、绿、蓝和透明度四个分量,即 RGB 颜色模型。在 Quartz 中,每个分量用 CGFloat 表示,值范围在 0.0 到 1.0 之间。

将红、绿、蓝三种颜色的光按相同比例混合,根据光的强度,人眼看到的结果可能是白色或灰色。按不同比例组合这三种加色原色可得到一系列不同的颜色,称为色域。

在小学可能学过原色是红、黄、蓝,即 RYB 颜色模型,但在现代颜色理论和计算机图形中,这种模型应用很少,其色域比 RGB 颜色模型更有限,也不易用数学定义。

除了红、绿、蓝,Quartz 还使用透明度分量 alpha 表示颜色的透明度。当在另一种颜色上绘制颜色时, alpha 用于确定最终绘制的颜色。 alpha 为 1.0 时,绘制的颜色完全不透明,会遮挡下面的颜色;小于 1.0 时,下面的颜色会透过来与上面的颜色混合; alpha 为 0.0 时,颜色完全透明,后面的内容会完全显示出来。使用 alpha 分量时,颜色模型有时称为 RGBA 颜色模型。

7. 其他颜色模型

除了 RGB 模型,还有其他颜色模型在使用,如下表所示:
| 颜色模型 | 说明 |
| ---- | ---- |
| 色相、饱和度、值 (HSV) | - |
| 色相、饱和度、亮度 (HSL) | - |
| 青色、品红色、黄色、黑色 (CMYK) | 用于四色胶印 |
| 灰度 | - |

不过,大多数操作无需担心使用的颜色模型,可直接调用 UIColor 对象的 CGColor ,Core Graphics 会处理必要的转换。

8. 颜色便捷方法

UIColor 有很多便捷方法,可返回初始化为特定颜色的 UIColor 对象。例如使用 redColor 方法将颜色初始化为红色。大多数便捷方法创建的 UIColor 实例使用 RGBA 颜色模型,例外的是表示灰度值的预定义 UIColor ,如 blackColor whiteColor darkGrayColor ,它们仅根据白色级别和透明度定义。

如果需要更精确地控制颜色,可通过指定四个分量来创建颜色,示例如下:

UIColor *red = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0];
9. 在上下文中绘制图像

Quartz 允许直接在上下文中绘制图像,可使用 UIImage 类替代 Core Graphics 数据结构 CGImage UIImage 类包含将其图像绘制到当前上下文的方法,可通过以下两种技术指定图像在上下文中的位置:
- 指定 CGPoint 来确定图像的左上角。
- 指定 CGRect 来框定图像,必要时调整图像大小以适应框架。

示例代码如下:

UIImage *image; // 假设该对象存在并指向一个 UIImage 实例
CGPoint drawPoint = CGPointMake(100.0, 100.0);
[image drawAtPoint:drawPoint];
10. 绘制形状:多边形、线条和曲线

Quartz 提供了许多函数,便于创建复杂形状。例如,绘制椭圆时,只需定义椭圆需要适配的矩形,Core Graphics 会完成其余工作:

CGRect theRect = CGRectMake(0, 0, 100, 100);
CGContextAddEllipseInRect(context, theRect);
CGContextDrawPath(context, kCGPathFillStroke);

绘制矩形也可使用类似方法,Quartz 还提供了创建更复杂形状(如弧和贝塞尔路径)的方法。

11. Quartz 2D 工具示例:图案、渐变和虚线图案

Quartz 提供了一系列强大的工具,例如支持用渐变填充多边形,除了绘制实线,还能使用各种虚线图案。

12. QuartzFun 应用程序

接下来将构建一个简单的绘图程序 QuartzFun,该应用程序顶部和底部各有一个分段控件,顶部控件可更改绘图颜色,底部控件可更改要绘制的形状。触摸并拖动时,将以所选颜色绘制所选形状,为简化应用程序,一次只绘制一个形状。

13. 设置 QuartzFun 应用程序

在 Xcode 中,使用单视图应用程序模板创建一个新项目,命名为 QuartzFun。该模板已提供应用程序委托和视图控制器,还需要创建一个 UIView 的子类,在其中通过重写 drawRect: 方法进行自定义绘图。具体步骤如下:
1. 选择 QuartzFun 文件夹(当前包含应用程序委托和视图控制器文件的文件夹),按 ⌘ + N 打开新文件助手,从 iOS 部分选择 Cocoa Touch 类,将新类命名为 QuartzFunView,并使其成为 UIView 的子类。
2. 定义一些常量,由于这些常量需要被多个类使用,因此创建一个专门的头文件。再次选择 QuartzFun 组,按 ⌘ + N 打开新文件助手,从 iOS 部分选择头文件模板,将文件命名为 Constants.h。
3. 还需要创建两个文件。因为应用程序提供了选择随机颜色的选项,而 UIColor 没有返回随机颜色的方法,所以需要编写代码实现。将代码放在 UIColor 的一个类别中,具体操作如下:
- 再次选择 QuartzFun 文件夹,按 ⌘ + N 打开新文件助手,选择 iOS 下的 Objective-C 文件,点击下一步。
- 提示时,将文件命名为 Random,将文件类型设置为类别,类设置为 UIColor,然后点击下一步并将文件保存在项目文件夹中。

14. 创建随机颜色

首先处理类别,在 UIColor+Random.h 文件中添加以下代码:

#import <UIKit/UIKit.h>

@interface UIColor (Random)
+ (UIColor *)randomColor;
@end

然后在 UIColor+Random.m 文件中添加以下代码:

#import <Foundation/Foundation.h>
#import "UIColor+Random.h"

@implementation UIColor (Random)

+ (UIColor *)randomColor {
    CGFloat red = (CGFloat)(arc4random() % 256)/255;
    CGFloat blue = (CGFloat)(arc4random() % 256)/255;
    CGFloat green = (CGFloat)(arc4random() % 256)/255;
    return [UIColor colorWithRed:red green:green blue:blue alpha:1.0f];
}

@end

以下是创建 QuartzFun 应用程序的流程图:

graph LR
    A[在 Xcode 中使用单视图应用程序模板创建新项目 QuartzFun] --> B[创建 UIView 子类 QuartzFunView]
    B --> C[创建常量头文件 Constants.h]
    C --> D[创建 UIColor 类别 Random]
    D --> E[在 UIColor+Random.h 中定义随机颜色方法]
    E --> F[在 UIColor+Random.m 中实现随机颜色方法]

综上所述,通过以上步骤和代码,我们可以利用 Core Graphics 和 Quartz 2D 构建一个简单的绘图应用程序,实现自定义绘图功能。在实际开发中,可根据需求进一步扩展和优化这些功能。

利用 Core Graphics 进行绘图

15. 实现 QuartzFunView 的绘图逻辑

前面我们创建了 QuartzFunView 这个自定义视图类,接下来要在 drawRect: 方法中实现具体的绘图逻辑。在这个方法里,我们会根据用户选择的颜色和形状进行绘图。

以下是 QuartzFunView.m 文件中 drawRect: 方法的示例代码:

#import "QuartzFunView.h"
#import "Constants.h"

@implementation QuartzFunView

- (void)drawRect:(CGRect)rect {
    // 获取当前图形上下文
    CGContextRef context = UIGraphicsGetCurrentContext();

    // 设置绘图颜色
    UIColor *currentColor = [self currentColor];
    CGContextSetStrokeColorWithColor(context, currentColor.CGColor);
    CGContextSetFillColorWithColor(context, currentColor.CGColor);

    // 根据选择的形状进行绘图
    switch (self.currentShape) {
        case kShapeRectangle: {
            CGRect shapeRect = CGRectInset(rect, 20, 20);
            CGContextAddRect(context, shapeRect);
            CGContextDrawPath(context, kCGPathFillStroke);
            break;
        }
        case kShapeEllipse: {
            CGRect shapeRect = CGRectInset(rect, 20, 20);
            CGContextAddEllipseInRect(context, shapeRect);
            CGContextDrawPath(context, kCGPathFillStroke);
            break;
        }
        case kShapeLine: {
            CGContextSetLineWidth(context, 4.0);
            CGContextMoveToPoint(context, CGRectGetMinX(rect) + 20, CGRectGetMidY(rect));
            CGContextAddLineToPoint(context, CGRectGetMaxX(rect) - 20, CGRectGetMidY(rect));
            CGContextStrokePath(context);
            break;
        }
        default:
            break;
    }
}

- (UIColor *)currentColor {
    // 这里可以根据实际情况返回当前选择的颜色
    return [UIColor redColor];
}

@end

上述代码中, drawRect: 方法的执行步骤如下:
1. 获取当前图形上下文。
2. 设置绘图的描边颜色和填充颜色。
3. 根据 currentShape 属性的值,选择不同的形状进行绘图。
- 矩形:使用 CGContextAddRect 方法添加矩形路径,然后使用 CGContextDrawPath 方法绘制。
- 椭圆:使用 CGContextAddEllipseInRect 方法添加椭圆路径,然后使用 CGContextDrawPath 方法绘制。
- 线条:设置线宽,使用 CGContextMoveToPoint CGContextAddLineToPoint 方法创建线条路径,最后使用 CGContextStrokePath 方法绘制。

16. 连接界面元素与代码

我们的 QuartzFun 应用程序有顶部和底部的分段控件,分别用于选择颜色和形状。接下来要将这些界面元素与代码进行连接,实现交互功能。

ViewController.m 文件中,我们可以通过以下代码实现:

#import "ViewController.h"
#import "QuartzFunView.h"
#import "Constants.h"

@interfaceViewController ()
@property (weak, nonatomic) IBOutlet UISegmentedControl *colorSegmentedControl;
@property (weak, nonatomic) IBOutlet UISegmentedControl *shapeSegmentedControl;
@property (weak, nonatomic) IBOutlet QuartzFunView *quartzFunView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 初始化选择
    [self updateColor];
    [self updateShape];
}

- (IBAction)changeColor:(UISegmentedControl *)sender {
    [self updateColor];
    [self.quartzFunView setNeedsDisplay];
}

- (IBAction)changeShape:(UISegmentedControl *)sender {
    [self updateShape];
    [self.quartzFunView setNeedsDisplay];
}

- (void)updateColor {
    NSInteger selectedIndex = self.colorSegmentedControl.selectedSegmentIndex;
    UIColor *color;
    switch (selectedIndex) {
        case kColorRed:
            color = [UIColor redColor];
            break;
        case kColorGreen:
            color = [UIColor greenColor];
            break;
        case kColorBlue:
            color = [UIColor blueColor];
            break;
        case kColorRandom:
            color = [UIColor randomColor];
            break;
        default:
            color = [UIColor redColor];
            break;
    }
    self.quartzFunView.currentColor = color;
}

- (void)updateShape {
    NSInteger selectedIndex = self.shapeSegmentedControl.selectedSegmentIndex;
    self.quartzFunView.currentShape = selectedIndex;
}

@end

上述代码的主要逻辑如下:
1. 在 viewDidLoad 方法中,初始化颜色和形状的选择。
2. changeColor: changeShape: 方法分别处理颜色和形状分段控件的选择变化,调用 updateColor updateShape 方法更新视图的属性,并调用 setNeedsDisplay 方法触发视图重绘。
3. updateColor 方法根据颜色分段控件的选择,设置 QuartzFunView currentColor 属性。
4. updateShape 方法根据形状分段控件的选择,设置 QuartzFunView currentShape 属性。

17. 总结与拓展

通过以上步骤,我们成功构建了一个简单的绘图应用程序 QuartzFun ,利用 Core Graphics 和 Quartz 2D 实现了自定义绘图功能。以下是整个开发过程的总结表格:
| 步骤 | 操作 | 代码文件 |
| ---- | ---- | ---- |
| 1 | 创建项目和自定义视图类 | Xcode、QuartzFunView.h、QuartzFunView.m |
| 2 | 创建常量头文件 | Xcode、Constants.h |
| 3 | 创建 UIColor 类别实现随机颜色 | Xcode、UIColor+Random.h、UIColor+Random.m |
| 4 | 实现 QuartzFunView 的绘图逻辑 | QuartzFunView.m |
| 5 | 连接界面元素与代码 | ViewController.m |

在实际开发中,我们可以对这个应用程序进行进一步的拓展,例如:
- 增加更多的形状和绘图工具,如多边形、曲线等。
- 实现图形的缩放、旋转等变换功能。
- 支持多点触摸,实现多人同时绘图。

以下是拓展功能实现的流程图:

graph LR
    A[现有 QuartzFun 应用程序] --> B[增加更多形状和绘图工具]
    A --> C[实现图形变换功能]
    A --> D[支持多点触摸]
    B --> E[更新绘图逻辑和界面]
    C --> F[添加变换矩阵和手势识别]
    D --> G[处理多点触摸事件]

通过不断地拓展和优化,我们可以利用 Core Graphics 和 Quartz 2D 开发出功能丰富、交互性强的绘图应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值