iOS开发基础110-Core Graphics应用场景

Core Graphics是一种强大的二维图形绘制框架,广泛应用于iOS开发中。以下是几个常见的运用场景以及对应的代码示例:

1. 自定义视图绘制

通过覆盖UIView的drawRect:方法,可以自定义视图的外观。

示例代码:

#import <UIKit/UIKit.h>

@interface CustomView : UIView

@end

@implementation CustomView

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // Set fill color
    CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
    
    // Draw a filled rectangle
    CGContextFillRect(context, CGRectMake(20, 20, 100, 100));
    
    // Set stroke color
    CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
    
    // Draw a circle
    CGContextStrokeEllipseInRect(context, CGRectMake(150, 20, 100, 100));
}

@end

2. 绘制图像

使用Core Graphics可以在视图中绘制图像,并进行一些基本的图像处理操作。

示例代码:

#import <UIKit/UIKit.h>

@interface ImageView : UIView

@end

@implementation ImageView

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    UIImage *image = [UIImage imageNamed:@"example.png"];
    
    // Draw image in the center of the view
    CGRect imageRect = CGRectMake((self.bounds.size.width - image.size.width) / 2,
                                  (self.bounds.size.height - image.size.height) / 2,
                                  image.size.width,
                                  image.size.height);
    CGContextDrawImage(context, imageRect, image.CGImage);
}

@end

3. 绘制文本

使用Core Graphics可以自定义文本的绘制,包括设置字体、颜色、对齐方式等。

示例代码:

#import <UIKit/UIKit.h>

@interface TextView : UIView

@end

@implementation TextView

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
    
    NSString *text = @"Hello, Core Graphics!";
    UIFont *font = [UIFont systemFontOfSize:24];
    NSDictionary *attributes = @{NSFontAttributeName: font, NSForegroundColorAttributeName: [UIColor blackColor]};
    
    CGSize textSize = [text sizeWithAttributes:attributes];
    CGRect textRect = CGRectMake((self.bounds.size.width - textSize.width) / 2,
                                 (self.bounds.size.height - textSize.height) / 2,
                                 textSize.width,
                                 textSize.height);
    [text drawInRect:textRect withAttributes:attributes];
}

@end

4. 绘制渐变

通过Core Graphics可以绘制线性或径向渐变。

示例代码:

#import <UIKit/UIKit.h>

@interface GradientView : UIView

@end

@implementation GradientView

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    NSArray *colors = @[(__bridge id)[UIColor redColor].CGColor,
                        (__bridge id)[UIColor blueColor].CGColor];
    
    CGFloat locations[] = { 0.0, 1.0 };
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
    
    CGPoint startPoint = CGPointMake(0, 0);
    CGPoint endPoint = CGPointMake(self.bounds.size.width, self.bounds.size.height);
    
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}

@end

5. 绘制路径

使用Core Graphics可以创建复杂路径,包括直线、曲线等。

示例代码:

#import <UIKit/UIKit.h>

@interface PathView : UIView

@end

@implementation PathView

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
    CGContextSetLineWidth(context, 2.0);
    
    CGContextMoveToPoint(context, 20, 20);
    CGContextAddLineToPoint(context, 200, 20);
    CGContextAddCurveToPoint(context, 200, 70, 50, 70, 50, 120);
    CGContextAddArc(context, 100, 100, 50, 0, M_PI, 0);
    
    CGContextStrokePath(context);
}

@end

6. 图像掩码和裁剪

可以使用Core Graphics进行图像的裁剪和掩码处理,用于创建特殊效果或剪裁图像形状。

示例代码:

- (void)drawRect:(CGRect)rect {
    // 获得当前上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // 创建并设置图像掩码
    CGImageRef maskImage = [[UIImage imageNamed:@"maskImage.png"] CGImage];
    CGContextClipToMask(context, self.bounds, maskImage);
    
    // 绘制图像
    UIImage *image = [UIImage imageNamed:@"background.jpg"];
    CGRect imageRect = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
    CGContextDrawImage(context, imageRect, image.CGImage);
}

7. 绘制阴影

Core Graphics 也允许你在绘图时添加阴影效果,增强视觉层次感。

示例代码:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetShadowWithColor(context, CGSizeMake(5, 5), 10, [UIColor grayColor].CGColor);
    
    // 绘制一个圆形,它会有阴影效果
    CGContextFillEllipseInRect(context, CGRectMake(50, 50, 100, 100));
}

8. 使用图层(Layer)

尽管Core Graphics主要集中在即时绘图上,但是通过与CALayer的合作使用,可以创建复杂且高效的动画和其他视觉效果。

示例代码:

- (void)setupLayerDrawing {
    CALayer *customLayer = [CALayer layer];
    customLayer.frame = CGRectMake(50, 50, 100, 100);
    customLayer.delegate = self;
    [self.layer addSublayer:customLayer];
    [customLayer display]; // 强制图层重新绘制,会调用代理的drawLayer:inContext:方法
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
    CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
    CGContextFillRect(ctx, layer.bounds);
}

9. 处理触摸事件

Core Graphics也可用于响应触摸事件,例如在用户触摸界面的指定区域时绘制图形或者线条。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self];
    // 在触摸开始的地方绘制
    // 这里是代码的简要概述,具体实现时,你可能需要将绘制逻辑放入drawRect:方法
    // 并调用[self setNeedsDisplay]来触发重绘
}

10. PDF内容创建与绘制

Core Graphics提供了生成PDF文件和在PDF上绘制内容的功能,使得在应用内处理PDF变得可能。

- (void)createPDF {
    NSString *pdfFilePath = ...; // PDF文件的路径
    UIGraphicsBeginPDFContextToFile(pdfFilePath, CGRectZero, nil);
    
    UIGraphicsBeginPDFPage();
    CGContextRef pdfContext = UIGraphicsGetCurrentContext();
    
    // 在这里使用Core Graphics画图...
    
    UIGraphicsEndPDFContext();
}

11. 绘制带圆角矩形

可以使用Core Graphics绘制带有圆角的矩形,这对于创建具有现代UI设计的自定义视图非常有用。

示例代码

- (void)drawRoundedRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    float radius = 10.0; // 圆角的半径
    
    CGContextBeginPath(context);
    CGContextMoveToPoint(context, CGRectGetMinX(rect) + radius, CGRectGetMinY(rect));
    CGContextAddArcToPoint(context, CGRectGetMaxX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMaxY(rect), radius);
    CGContextAddArcToPoint(context, CGRectGetMaxX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMaxY(rect), radius);
    CGContextAddArcToPoint(context, CGRectGetMinX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMinY(rect), radius);
    CGContextAddArcToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMinY(rect), radius);
    CGContextClosePath(context);
    
    CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextFillPath(context);
}

上述代码通过使用CGContextAddArcToPoint函数来创建一个具有圆角的矩形路径,并以填充模式绘制该路径。

12. 绘制渐变填充圆形

绘制一个圆形并使用线性渐变进行填充,可以创建现代化的进度指示器或背景。

示例代码

- (void)drawGradientCircleInRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    NSArray *colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor blueColor].CGColor];
    CGFloat locations[] = { 0.0, 1.0 };
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
    
    CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
    float radius = MIN(rect.size.width, rect.size.height) / 2;
    
    CGContextSaveGState(context);
    CGContextAddArc(context, center.x, center.y, radius, 0, 2*M_PI, 1);
    CGContextClip(context);
    
    CGPoint startPoint = center;
    CGPoint endPoint = CGPointMake(center.x, center.y - radius);
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    
    CGContextRestoreGState(context);
    
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}

此代码段首先创建一个圆形路径,然后使用CGContextClip函数裁剪上下文以便渐变只填充该圆形范围。接着,定义一个线性渐变并在圆形内部绘制,从而实现内部填充效果。

13. 绘制和填充贝塞尔曲线

贝塞尔曲线是构建复杂形状的有力工具,Core Graphics使其绘制变得容易。

示例代码

- (void)drawBezierCurveInRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextMoveToPoint(context, CGRectGetMinX(rect), CGRectGetMidY(rect));
    
    // 添加一个三次贝塞尔曲线
    CGContextAddCurveToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMidY(rect));
    
    // 添加一个二次贝塞尔曲线
    CGContextAddQuadCurveToPoint(context, CGRectGetMaxX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMidY(rect));
    
    CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
    CGContextFillPath(context);
}

在上述示例中,我们首先通过CGContextMoveToPoint移动到路径的起始点。接着,使用CGContextAddCurveToPoint添加一个三次贝塞尔曲线和CGContextAddQuadCurveToPoint添加一个二次贝塞尔曲线,并以填充方式完成绘制。

14. 创建一个自定义进度条

假设你想在应用内创建一个自定义的圆形进度条,展示某个任务的完成进度。下面的示例将指导你如何使用Core Graphics来实现这个目标。

1. 定义一个自定义视图

首先,定义一个UIView的子类,在其中添加进度条的绘制逻辑。

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

@interface CustomProgressView : UIView
@property (nonatomic) CGFloat progress; // 0.0 to 1.0
@end
// CustomProgressView.m
#import "CustomProgressView.h"

@implementation CustomProgressView

- (void)setProgress:(CGFloat)progress {
    _progress = progress;
    [self setNeedsDisplay]; // 当进度更新时,请求重新绘制视图
}

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    // Draw background circle
    CGContextSetStrokeColorWithColor(context, [UIColor lightGrayColor].CGColor);
    CGContextSetLineWidth(context, 10);
    CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
    CGFloat radius = (MIN(rect.size.width, rect.size.height) - 10) / 2;
    CGContextAddArc(context, center.x, center.y, radius, 0, 2 * M_PI, 0);
    CGContextStrokePath(context);
    
    // Draw progress
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    CGFloat startAngle = - M_PI_2; // 顶部开始
    CGFloat endAngle = startAngle + self.progress * 2 * M_PI;
    CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
    CGContextStrokePath(context);
}

@end

drawRect:方法中,我们首先绘制了一个背景圆环,接着根据progress属性的值绘制了一个代表进度的弧形。progress属性被设置为0.0至1.0之间,代表进度的百分比。当进度更新时,我们调用setNeedsDisplay方法来重绘视图。

15. 绘制带有文本的图形

有时,你可能希望在绘制的图形上显示文本信息。以下示例展示如何将文本绘制到一个自定义的视图中。

示例代码

在你的UIView子类中的drawRect:方法实现:

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    // 绘制一个圆形
    CGContextAddEllipseInRect(ctx, rect);
    CGContextSetFillColorWithColor(ctx, [UIColor orangeColor].CGColor);
    CGContextFillPath(ctx);
    
    // 准备文本属性
    NSString *string = @"Hello World";
    NSDictionary *attributes = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:16],
                                 NSForegroundColorAttributeName: [UIColor whiteColor]};
    
    // 计算文本绘制的位置
    CGSize stringSize = [string sizeWithAttributes:attributes];
    CGPoint stringPoint = CGPointMake(CGRectGetMidX(rect) - stringSize.width / 2,
                                      CGRectGetMidY(rect) - stringSize.height / 2);
    
    // 绘制文本
    [string drawAtPoint:stringPoint withAttributes:attributes];
}

这段代码先绘制了一个实心圆形,然后在其上居中绘制了一段文字。通过计算文本尺寸和视图的中点来确定文本的绘制位置,使文本能够恰当地居中显示。


16.高级路径绘制和图形变换

我们将展示如何绘制一个复杂的图形,并对其进行变换。这个例子会使用到路径创建、填充、描边、以及使用仿射变换对图形进行平移和旋转。

示例代码

我们将创建一个自定义的UIView,它会绘制一个星形图案,然后对星形应用旋转和平移变换。

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

@interface StarView : UIView
@end
// StarView.m
#import "StarView.h"

@implementation StarView

- (void)drawRect:(CGRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    CGFloat centerX = CGRectGetMidX(rect);
    CGFloat centerY = CGRectGetMidY(rect);
    CGFloat radius = MIN(rect.size.width, rect.size.height) / 3; // 星形的半径
    
    // 创建一个五角星形的路径
    CGMutablePathRef starPath = CGPathCreateMutable();
    CGPathMoveToPoint(starPath, NULL, centerX, centerY - radius);
    for (int i = 1; i < 5; ++i) {
        CGFloat x = sinf(i * 4.0 * M_PI / 5.0);
        CGFloat y = cosf(i * 4.0 * M_PI / 5.0);
        CGPathAddLineToPoint(starPath, NULL, centerX - radius * x, centerY - radius * y);
    }
    CGPathCloseSubpath(starPath);
    
    // 使用仿射变换对星形进行旋转和平移
    CGAffineTransform transform = CGAffineTransformIdentity;
    transform = CGAffineTransformTranslate(transform, 0, 40); // 下移40个单位
    transform = CGAffineTransformRotate(transform, M_PI / 5); // 旋转36度(即π/5弧度)
    CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(starPath, &transform);
    
    // 绘制星形
    CGContextAddPath(ctx, transformedPath);
    CGContextSetFillColorWithColor(ctx, [UIColor greenColor].CGColor);
    CGContextFillPath(ctx);
    CGContextAddPath(ctx, transformedPath);
    CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);
    CGContextStrokePath(ctx);
    
    CGPathRelease(starPath);
    CGPathRelease(transformedPath);
}

@end

该示例中,首先创建了一个五角星形的路径starPath,然后通过CGAffineTransform创建了一个变换,将星形下移并旋转。使用CGPathCreateCopyByTransformingPath()函数应用这个变换到路径上,并通过Core Graphics的上下文将其绘制到视图上。

使用图层蒙版和阴影效果

通过结合使用Core Graphics和Core Animation,我们可以实现一些高级的渲染效果,如蒙版和阴影。

示例代码

这个示例将向你展示如何给一个UIView的子类添加阴影效果,并使用图层蒙版来显示一部分内容。

- (void)setupShadowAndMask {
    CALayer *layer = self.layer;
    layer.shadowOpacity = 0.5;
    layer.shadowRadius = 5.0;
    layer.shadowOffset = CGSizeMake(5.0, 5.0);
    layer.shadowColor = [UIColor blackColor].CGColor;
    
    // 创建一个圆形蒙版
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    CGPathRef circlePath = CGPathCreateWithEllipseInRect(self.bounds, NULL);
    maskLayer.path = circlePath;
    layer.mask = maskLayer;
    
    CGPathRelease(circlePath);
}

在这个示例中,首先对视图的根层(CALayer)设置了阴影效果,包括阴影的透明度、半径、偏移量和颜色。然后创建了一个CAShapeLayer图层作为蒙版,使用一个椭圆路径来限制只在这个椭圆路径范围内显示内容。最后,将这个蒙版图层设置给根层的mask属性。


理解了基本的绘制技巧和高级效果之后,让我们尝试一个更富有挑战性、也更加“吊炸天”的使用场景:创建一个动态波浪效果的视图,这个视图可以用作动态背景,展示Loading状态,或者在音乐应用中表示音量变化等。

17.动态波浪效果

我们将创建一个自定义的UIView,内部使用CADisplayLink来实现一个连续动态的波浪效果。这个波浪效果使用正弦函数生成,可通过调整相关参数来模拟水波的动态。以下是实现这一效果的步骤及相关代码。

定义自定义视图

首先,定义一个UIView的子类WaveView来封装波浪效果的绘制逻辑。

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

@interface WaveView : UIView

@property (nonatomic, strong) UIColor *waveColor; // 波浪颜色
- (void)startWave;
- (void)stopWave;

@end

WaveView的实现中,我们使用CADisplayLink来周期性更新波浪的位置,实现动态效果。

// WaveView.m
#import "WaveView.h"

@interface WaveView ()
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) CGFloat offset; // 波浪水平偏移
@end

@implementation WaveView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.waveColor = [UIColor blueColor];
        self.offset = 0;
    }
    return self;
}

- (void)startWave {
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(wave)];
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)stopWave {
    [self.displayLink invalidate];
    self.displayLink = nil;
}

- (void)wave {
    // 更新波浪的水平偏移
    self.offset += 10;
    
    // 重绘
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGMutablePathRef path = CGPathCreateMutable();
    
    CGFloat startY = self.bounds.size.height / 2; // 起始Y坐标
    CGPathMoveToPoint(path, NULL, 0, startY);
    for (CGFloat x = 0; x <= CGRectGetWidth(self.bounds); x++) {
        // 波浪公式:y = A * sin(ωx + φ) + k
        CGFloat y = 20 * sin(0.01 * self.offset + x * 0.02) + startY;
        CGPathAddLineToPoint(path, NULL, x, y);
    }
    
    // 封闭路径并填充颜色
    CGPathAddLineToPoint(path, NULL, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
    CGPathAddLineToPoint(path, NULL, 0, CGRectGetHeight(self.bounds));
    CGPathCloseSubpath(path);
    CGContextAddPath(context, path);
    CGContextSetFillColorWithColor(context, self.waveColor.CGColor);
    CGContextFillPath(context);
    
    CGPathRelease(path);
}

@end

在上述代码中,startWave方法会启动一个CADisplayLink实例周期性调用wave方法,后者通过增加水平偏移self.offset来动态更新波浪路径,并请求重新绘制UIView。drawRect:方法会根据当前的水平偏移计算每个点的Y坐标,绘制出动态变化的波浪效果。

使用

你可以在需要动态波浪效果的地方,创建WaveView的实例,并调用startWave方法开始动画,使用stopWave方法停止动画。

WaveView *waveView = [[WaveView alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
[self.view addSubview:waveView];
[waveView startWave];

小结

这个示例演示了如何结合使用Core Graphics和CADisplayLink来创建一个动态且吸引人的视觉效果。通过调整波浪动态的参数,你可以创建出多种不同风格的波浪效果,为你的应用提供丰富且美观的视觉展现。

posted @   Mr.陳  阅读(75)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
历史上的今天:
2015-07-17 iOS开发基础10-UIButton内边距和图片拉伸模式
2015-07-17 iOS开发基础9-提示框(UIAlertController)
点击右上角即可分享
微信分享提示