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
来创建一个动态且吸引人的视觉效果。通过调整波浪动态的参数,你可以创建出多种不同风格的波浪效果,为你的应用提供丰富且美观的视觉展现。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
2015-07-17 iOS开发基础10-UIButton内边距和图片拉伸模式
2015-07-17 iOS开发基础9-提示框(UIAlertController)