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

Objective-C中的Core Graphics绘图技术

2023-06-156.6k 阅读

Core Graphics基础

Core Graphics,也称为Quartz 2D,是一个强大的二维绘图引擎,它提供了一套基于路径的绘图模型,可用于在iOS和macOS应用程序中创建高质量的图形。在Objective-C中使用Core Graphics,你可以绘制各种形状、图像、文本等。

图形上下文(Graphics Context)

图形上下文是Core Graphics绘图的核心概念之一。它是一个包含绘图状态信息的对象,例如当前的颜色、线条宽度、字体等。在开始绘图之前,你需要获取一个图形上下文。在iOS中,通常在视图的drawRect:方法中获取图形上下文,如下所示:

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

在macOS中,获取图形上下文的方式稍有不同:

- (void)drawRect:(NSRect)dirtyRect {
    NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
    CGContextRef context = [graphicsContext graphicsPort];
    // 绘图操作
}

获取到图形上下文后,你就可以使用Core Graphics的函数对其进行操作。

路径(Paths)

路径是Core Graphics中用于定义形状的基本元素。路径由直线、曲线和控制点组成。你可以通过CGContextMoveToPointCGContextAddLineToPointCGContextAddArc等函数来创建路径。

例如,绘制一个简单的矩形路径:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGRect rectangle = CGRectMake(50, 50, 200, 150);
    CGContextAddRect(context, rectangle);
    
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    CGContextStrokePath(context);
}

在上述代码中,CGContextAddRect函数用于添加一个矩形路径到图形上下文。CGContextSetStrokeColorWithColor设置线条颜色,CGContextSetLineWidth设置线条宽度,最后CGContextStrokePath绘制路径的轮廓。

填充(Filling)

除了绘制路径的轮廓,你还可以对路径进行填充。Core Graphics提供了CGContextFillPath函数用于填充路径。例如,填充上述矩形:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGRect rectangle = CGRectMake(50, 50, 200, 150);
    CGContextAddRect(context, rectangle);
    
    CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
    CGContextFillPath(context);
}

如果既想要绘制轮廓又想要填充,可以使用CGContextDrawPath函数,并指定kCGPathFillStroke模式:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGRect rectangle = CGRectMake(50, 50, 200, 150);
    CGContextAddRect(context, rectangle);
    
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
    
    CGContextDrawPath(context, kCGPathFillStroke);
}

绘制线条和形状

绘制直线

绘制直线是Core Graphics中最基本的操作之一。你可以通过CGContextMoveToPoint指定起点,然后使用CGContextAddLineToPoint指定终点来绘制直线。例如:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextMoveToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 300, 300);
    
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
    CGContextSetLineWidth(context, 3.0);
    CGContextStrokePath(context);
}

上述代码绘制了一条从点(100, 100)到点(300, 300)的红色直线,线条宽度为3。

绘制弧线

绘制弧线可以使用CGContextAddArc函数。该函数需要指定圆心坐标、半径、起始角度和结束角度等参数。例如,绘制一个半圆:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGFloat centerX = CGRectGetMidX(rect);
    CGFloat centerY = CGRectGetMidY(rect);
    CGFloat radius = 100;
    CGFloat startAngle = 0;
    CGFloat endAngle = M_PI;
    
    CGContextAddArc(context, centerX, centerY, radius, startAngle, endAngle, 0);
    
    CGContextSetStrokeColorWithColor(context, [UIColor purpleColor].CGColor);
    CGContextSetLineWidth(context, 4.0);
    CGContextStrokePath(context);
}

在这个例子中,我们以视图的中心为圆心,半径为100绘制了一个半圆,线条颜色为紫色,宽度为4。

绘制多边形

绘制多边形需要依次添加各个顶点的坐标。例如,绘制一个三角形:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextMoveToPoint(context, 150, 50);
    CGContextAddLineToPoint(context, 250, 250);
    CGContextAddLineToPoint(context, 50, 250);
    CGContextClosePath(context);
    
    CGContextSetStrokeColorWithColor(context, [UIColor orangeColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
    CGContextDrawPath(context, kCGPathFillStroke);
}

这里使用CGContextClosePath函数将最后一个顶点与起点连接起来,形成一个封闭的三角形,并同时进行填充和绘制轮廓。

绘制文本

设置字体和文本属性

在Core Graphics中绘制文本,首先需要设置字体和文本相关属性。你可以使用CTFontCreateWithName创建字体对象,并使用CTTextAlignment设置文本对齐方式。例如:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CFStringRef fontName = CFSTR("Helvetica-Bold");
    CGFloat fontSize = 24;
    CTFontRef font = CTFontCreateWithName(fontName, fontSize, NULL);
    
    NSDictionary *attributes = @{(id)kCTFontAttributeName : (__bridge id)font,
                                 (id)kCTForegroundColorAttributeName : (__bridge id)[UIColor blackColor].CGColor};
    
    CFRelease(font);
    // 在这里绘制文本
}

绘制字符串

有了字体和属性后,就可以使用CTFramesetterCreateWithAttributedStringCTFrameDraw函数来绘制字符串。例如:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CFStringRef fontName = CFSTR("Helvetica-Bold");
    CGFloat fontSize = 24;
    CTFontRef font = CTFontCreateWithName(fontName, fontSize, NULL);
    
    NSDictionary *attributes = @{(id)kCTFontAttributeName : (__bridge id)font,
                                 (id)kCTForegroundColorAttributeName : (__bridge id)[UIColor blackColor].CGColor};
    
    NSString *text = @"Hello, Core Graphics!";
    NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:attributes];
    
    CFRelease(font);
    
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attributedText);
    
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, rect);
    
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
    
    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0, rect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    
    CTFrameDraw(frame, context);
    
    CGContextRestoreGState(context);
    
    CFRelease(frame);
    CFRelease(path);
    CFRelease(framesetter);
}

在上述代码中,我们首先创建了一个字体和属性字典,然后使用这些属性创建了一个带属性的字符串。接着,通过CTFramesetterCTFrame将字符串绘制到指定的矩形区域内。注意,在绘制之前需要对上下文进行一些变换,以确保文本的方向正确。

绘制图像

加载图像

在Objective-C中,可以使用UIImage(iOS)或NSImage(macOS)来加载图像。例如,在iOS中加载一个本地图像:

UIImage *image = [UIImage imageNamed:@"example.jpg"];

在macOS中加载图像:

NSImage *image = [[NSImage alloc] initWithContentsOfFile:@"example.jpg"];

绘制图像到上下文

加载图像后,可以使用CGContextDrawImage函数将图像绘制到图形上下文。例如,在iOS中:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    UIImage *image = [UIImage imageNamed:@"example.jpg"];
    CGImageRef cgImage = image.CGImage;
    
    CGRect imageRect = CGRectMake(50, 50, image.size.width, image.size.height);
    CGContextDrawImage(context, imageRect, cgImage);
}

在macOS中:

- (void)drawRect:(NSRect)dirtyRect {
    NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
    CGContextRef context = [graphicsContext graphicsPort];
    
    NSImage *image = [[NSImage alloc] initWithContentsOfFile:@"example.jpg"];
    [image drawInRect:NSMakeRect(50, 50, image.size.width, image.size.height) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}

在iOS中,CGContextDrawImage直接将CGImage绘制到指定的矩形区域。在macOS中,NSImagedrawInRect:fromRect:operation:fraction:方法提供了更灵活的绘制选项,例如可以指定混合模式和透明度等。

图形变换

平移变换(Translation)

平移变换可以将图形在上下文中移动到新的位置。使用CGContextTranslateCTM函数来实现平移。例如,将绘制的矩形向右平移100个单位,向下平移50个单位:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextTranslateCTM(context, 100, 50);
    
    CGRect rectangle = CGRectMake(0, 0, 200, 150);
    CGContextAddRect(context, rectangle);
    
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    CGContextStrokePath(context);
}

在上述代码中,CGContextTranslateCTM将当前上下文的原点移动到(100, 50),之后绘制的矩形将相对于新的原点。

缩放变换(Scaling)

缩放变换可以改变图形的大小。使用CGContextScaleCTM函数实现缩放。例如,将绘制的图形在水平和垂直方向上都放大2倍:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextScaleCTM(context, 2.0, 2.0);
    
    CGRect rectangle = CGRectMake(50, 50, 100, 50);
    CGContextAddRect(context, rectangle);
    
    CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    CGContextStrokePath(context);
}

这里CGContextScaleCTM将图形在x和y方向上都放大了2倍,原本大小为(100, 50)的矩形看起来将变为(200, 100)。

旋转变换(Rotation)

旋转变换可以围绕一个点旋转图形。使用CGContextRotateCTM函数实现旋转。例如,将绘制的图形围绕原点顺时针旋转45度:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGFloat angle = M_PI_4; // 45度
    CGContextRotateCTM(context, angle);
    
    CGRect rectangle = CGRectMake(50, 50, 100, 50);
    CGContextAddRect(context, rectangle);
    
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    CGContextStrokePath(context);
}

在上述代码中,CGContextRotateCTM将上下文顺时针旋转了45度(M_PI_4弧度),之后绘制的矩形将按此角度旋转。

透明度和混合模式

设置透明度

在Core Graphics中,可以通过CGContextSetAlpha函数设置图形的透明度。例如,绘制一个半透明的矩形:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetAlpha(context, 0.5);
    
    CGRect rectangle = CGRectMake(50, 50, 200, 150);
    CGContextAddRect(context, rectangle);
    
    CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextFillPath(context);
}

这里CGContextSetAlpha将透明度设置为0.5,使得绘制的蓝色矩形呈现半透明效果。

混合模式

混合模式决定了新绘制的图形与已存在图形的融合方式。Core Graphics提供了多种混合模式,例如kCGBlendModeNormalkCGBlendModeMultiplykCGBlendModeScreen等。使用CGContextSetBlendMode函数设置混合模式。例如,使用kCGBlendModeMultiply模式绘制两个重叠的矩形:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGRect rectangle1 = CGRectMake(50, 50, 150, 100);
    CGContextAddRect(context, rectangle1);
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillPath(context);
    
    CGContextSetBlendMode(context, kCGBlendModeMultiply);
    
    CGRect rectangle2 = CGRectMake(100, 100, 150, 100);
    CGContextAddRect(context, rectangle2);
    CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
    CGContextFillPath(context);
}

在上述代码中,第二个绿色矩形使用kCGBlendModeMultiply混合模式与第一个红色矩形进行混合,产生了不同的视觉效果。不同的混合模式适用于不同的场景,例如合成图像、创建特殊效果等。

高级绘图技巧

抗锯齿(Anti - Aliasing)

抗锯齿可以使绘制的图形边缘更加平滑。在Core Graphics中,默认情况下是开启抗锯齿的。但如果需要,可以通过设置图形上下文的属性来明确控制抗锯齿。例如,在iOS中:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetShouldAntialias(context, YES);
    
    // 绘制图形
}

在macOS中,可以通过NSGraphicsContext来设置抗锯齿:

- (void)drawRect:(NSRect)dirtyRect {
    NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
    [graphicsContext setShouldAntialias:YES];
    
    CGContextRef context = [graphicsContext graphicsPort];
    // 绘制图形
}

渐变(Gradients)

渐变可以创建平滑过渡的颜色效果。Core Graphics提供了CAGradientLayer(iOS)和NSGradient(macOS)来实现渐变效果。例如,在iOS中创建一个线性渐变:

- (void)drawRect:(CGRect)rect {
    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = rect;
    gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor blueColor].CGColor];
    gradientLayer.startPoint = CGPointMake(0, 0);
    gradientLayer.endPoint = CGPointMake(1, 1);
    
    [self.layer addSublayer:gradientLayer];
}

在macOS中创建线性渐变:

- (void)drawRect:(NSRect)dirtyRect {
    NSColor *startColor = [NSColor redColor];
    NSColor *endColor = [NSColor blueColor];
    NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:startColor endingColor:endColor];
    
    [gradient drawInRect:dirtyRect angle:45];
}

在iOS中,CAGradientLayer通过设置colorsstartPointendPoint来定义渐变的颜色和方向。在macOS中,NSGradientdrawInRect:angle:方法可以直接在指定矩形区域内绘制指定角度的渐变。

阴影(Shadows)

为图形添加阴影可以增强视觉效果。在Core Graphics中,可以通过设置图形上下文的阴影属性来实现。例如,在iOS中为绘制的矩形添加阴影:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetShadowWithColor(context, CGSizeMake(5, 5), 5, [UIColor grayColor].CGColor);
    
    CGRect rectangle = CGRectMake(50, 50, 200, 150);
    CGContextAddRect(context, rectangle);
    
    CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
    CGContextFillPath(context);
}

在上述代码中,CGContextSetShadowWithColor设置了阴影的偏移量(5, 5)、模糊半径5和颜色为灰色。之后绘制的绿色矩形将带有阴影效果。在macOS中,也有类似的方法可以设置阴影,通过NSGraphicsContextNSShadow对象来实现。

通过以上对Core Graphics绘图技术在Objective - C中的详细介绍,你可以利用这些知识创建出丰富多彩、高质量的图形界面和可视化内容,无论是简单的图形绘制还是复杂的动画和交互效果,Core Graphics都能提供强大的支持。在实际应用中,你可以根据具体需求灵活运用这些技术,结合Objective - C的面向对象特性,开发出优秀的iOS和macOS应用程序。