iOS开发基础110-Core Graphics应用场景
<p>Core Graphics是一种强大的二维图形绘制框架,广泛应用于iOS开发中。以下是几个常见的运用场景以及对应的代码示例:</p><h3 id="1-自定义视图绘制">1. 自定义视图绘制</h3>
<p>通过覆盖UIView的<code>drawRect:</code>方法,可以自定义视图的外观。</p>
<h4 id="示例代码">示例代码:</h4>
<pre><code class="language-objective-c">#import <UIKit/UIKit.h>
@interface CustomView : UIView
@end
@implementation CustomView
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// Set fill color
CGContextSetFillColorWithColor(context, .CGColor);
// Draw a filled rectangle
CGContextFillRect(context, CGRectMake(20, 20, 100, 100));
// Set stroke color
CGContextSetStrokeColorWithColor(context, .CGColor);
// Draw a circle
CGContextStrokeEllipseInRect(context, CGRectMake(150, 20, 100, 100));
}
@end
</code></pre>
<h3 id="2-绘制图像">2. 绘制图像</h3>
<p>使用Core Graphics可以在视图中绘制图像,并进行一些基本的图像处理操作。</p>
<h4 id="示例代码-1">示例代码:</h4>
<pre><code class="language-objective-c">#import <UIKit/UIKit.h>
@interface ImageView : UIView
@end
@implementation ImageView
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
UIImage *image = ;
// 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
</code></pre>
<h3 id="3-绘制文本">3. 绘制文本</h3>
<p>使用<code>Core Graphics</code>可以自定义文本的绘制,包括设置字体、颜色、对齐方式等。</p>
<h4 id="示例代码-2">示例代码:</h4>
<pre><code class="language-objective-c">#import <UIKit/UIKit.h>
@interface TextView : UIView
@end
@implementation TextView
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, .CGColor);
NSString *text = @"Hello, Core Graphics!";
UIFont *font = ;
NSDictionary *attributes = @{NSFontAttributeName: font, NSForegroundColorAttributeName: };
CGSize textSize = ;
CGRect textRect = CGRectMake((self.bounds.size.width - textSize.width) / 2,
(self.bounds.size.height - textSize.height) / 2,
textSize.width,
textSize.height);
;
}
@end
</code></pre>
<h3 id="4-绘制渐变">4. 绘制渐变</h3>
<p>通过<code>Core Graphics</code>可以绘制线性或径向渐变。</p>
<h4 id="示例代码-3">示例代码:</h4>
<pre><code class="language-objective-c">#import <UIKit/UIKit.h>
@interface GradientView : UIView
@end
@implementation GradientView
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSArray *colors = @[(__bridge id).CGColor,
(__bridge id).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
</code></pre>
<h3 id="5-绘制路径">5. 绘制路径</h3>
<p>使用<code>Core Graphics</code>可以创建复杂路径,包括直线、曲线等。</p>
<h4 id="示例代码-4">示例代码:</h4>
<pre><code class="language-objective-c">#import <UIKit/UIKit.h>
@interface PathView : UIView
@end
@implementation PathView
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, .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
</code></pre>
<h3 id="6-图像掩码和裁剪">6. 图像掩码和裁剪</h3>
<p>可以使用Core Graphics进行图像的裁剪和掩码处理,用于创建特殊效果或剪裁图像形状。</p>
<h4 id="示例代码-5">示例代码:</h4>
<pre><code class="language-objective-c">- (void)drawRect:(CGRect)rect {
// 获得当前上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 创建并设置图像掩码
CGImageRef maskImage = [ CGImage];
CGContextClipToMask(context, self.bounds, maskImage);
// 绘制图像
UIImage *image = ;
CGRect imageRect = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
CGContextDrawImage(context, imageRect, image.CGImage);
}
</code></pre>
<h3 id="7-绘制阴影">7. 绘制阴影</h3>
<p>Core Graphics 也允许你在绘图时添加阴影效果,增强视觉层次感。</p>
<h4 id="示例代码-6">示例代码:</h4>
<pre><code class="language-objective-c">- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShadowWithColor(context, CGSizeMake(5, 5), 10, .CGColor);
// 绘制一个圆形,它会有阴影效果
CGContextFillEllipseInRect(context, CGRectMake(50, 50, 100, 100));
}
</code></pre>
<h3 id="8-使用图层layer">8. 使用图层(Layer)</h3>
<p>尽管Core Graphics主要集中在即时绘图上,但是通过与CALayer的合作使用,可以创建复杂且高效的动画和其他视觉效果。</p>
<h4 id="示例代码-7">示例代码:</h4>
<pre><code class="language-objective-c">- (void)setupLayerDrawing {
CALayer *customLayer = ;
customLayer.frame = CGRectMake(50, 50, 100, 100);
customLayer.delegate = self;
;
; // 强制图层重新绘制,会调用代理的drawLayer:inContext:方法
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
CGContextSetFillColorWithColor(ctx, .CGColor);
CGContextFillRect(ctx, layer.bounds);
}
</code></pre>
<h3 id="9-处理触摸事件">9. 处理触摸事件</h3>
<p>Core Graphics也可用于响应触摸事件,例如在用户触摸界面的指定区域时绘制图形或者线条。</p>
<pre><code class="language-objective-c">- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = ;
CGPoint location = ;
// 在触摸开始的地方绘制
// 这里是代码的简要概述,具体实现时,你可能需要将绘制逻辑放入drawRect:方法
// 并调用来触发重绘
}
</code></pre>
<h3 id="10-pdf内容创建与绘制">10. PDF内容创建与绘制</h3>
<p>Core Graphics提供了生成PDF文件和在PDF上绘制内容的功能,使得在应用内处理PDF变得可能。</p>
<pre><code class="language-objective-c">- (void)createPDF {
NSString *pdfFilePath = ...; // PDF文件的路径
UIGraphicsBeginPDFContextToFile(pdfFilePath, CGRectZero, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// 在这里使用Core Graphics画图...
UIGraphicsEndPDFContext();
}
</code></pre>
<h3 id="11-绘制带圆角矩形">11. 绘制带圆角矩形</h3>
<p>可以使用Core Graphics绘制带有圆角的矩形,这对于创建具有现代UI设计的自定义视图非常有用。</p>
<h4 id="示例代码-8">示例代码</h4>
<pre><code class="language-objective-c">- (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, .CGColor);
CGContextFillPath(context);
}
</code></pre>
<p>上述代码通过使用<code>CGContextAddArcToPoint</code>函数来创建一个具有圆角的矩形路径,并以填充模式绘制该路径。</p>
<h3 id="12-绘制渐变填充圆形">12. 绘制渐变填充圆形</h3>
<p>绘制一个圆形并使用线性渐变进行填充,可以创建现代化的进度指示器或背景。</p>
<h4 id="示例代码-9">示例代码</h4>
<pre><code class="language-objective-c">- (void)drawGradientCircleInRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSArray *colors = @[(__bridge id).CGColor, (__bridge id).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);
}
</code></pre>
<p>此代码段首先创建一个圆形路径,然后使用<code>CGContextClip</code>函数裁剪上下文以便渐变只填充该圆形范围。接着,定义一个线性渐变并在圆形内部绘制,从而实现内部填充效果。</p>
<h3 id="13-绘制和填充贝塞尔曲线">13. 绘制和填充贝塞尔曲线</h3>
<p>贝塞尔曲线是构建复杂形状的有力工具,Core Graphics使其绘制变得容易。</p>
<h4 id="示例代码-10">示例代码</h4>
<pre><code class="language-objective-c">- (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, .CGColor);
CGContextFillPath(context);
}
</code></pre>
<p>在上述示例中,我们首先通过<code>CGContextMoveToPoint</code>移动到路径的起始点。接着,使用<code>CGContextAddCurveToPoint</code>添加一个三次贝塞尔曲线和<code>CGContextAddQuadCurveToPoint</code>添加一个二次贝塞尔曲线,并以填充方式完成绘制。</p>
<h3 id="14-创建一个自定义进度条">14. 创建一个自定义进度条</h3>
<p>假设你想在应用内创建一个自定义的圆形进度条,展示某个任务的完成进度。下面的示例将指导你如何使用Core Graphics来实现这个目标。</p>
<h4 id="1-定义一个自定义视图">1. 定义一个自定义视图</h4>
<p>首先,定义一个UIView的子类,在其中添加进度条的绘制逻辑。</p>
<pre><code class="language-objective-c">// CustomProgressView.h
#import <UIKit/UIKit.h>
@interface CustomProgressView : UIView
@property (nonatomic) CGFloat progress; // 0.0 to 1.0
@end
</code></pre>
<pre><code class="language-objective-c">// CustomProgressView.m
#import "CustomProgressView.h"
@implementation CustomProgressView
- (void)setProgress:(CGFloat)progress {
_progress = progress;
; // 当进度更新时,请求重新绘制视图
}
- (void)drawRect:(CGRect)rect {
;
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw background circle
CGContextSetStrokeColorWithColor(context, .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, .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
</code></pre>
<p>在<code>drawRect:</code>方法中,我们首先绘制了一个背景圆环,接着根据<code>progress</code>属性的值绘制了一个代表进度的弧形。<code>progress</code>属性被设置为0.0至1.0之间,代表进度的百分比。当进度更新时,我们调用<code>setNeedsDisplay</code>方法来重绘视图。</p>
<h3 id="15-绘制带有文本的图形">15. 绘制带有文本的图形</h3>
<p>有时,你可能希望在绘制的图形上显示文本信息。以下示例展示如何将文本绘制到一个自定义的视图中。</p>
<h4 id="示例代码-11">示例代码</h4>
<p>在你的UIView子类中的<code>drawRect:</code>方法实现:</p>
<pre><code class="language-objective-c">- (void)drawRect:(CGRect)rect {
;
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 绘制一个圆形
CGContextAddEllipseInRect(ctx, rect);
CGContextSetFillColorWithColor(ctx, .CGColor);
CGContextFillPath(ctx);
// 准备文本属性
NSString *string = @"Hello World";
NSDictionary *attributes = @{NSFontAttributeName: ,
NSForegroundColorAttributeName: };
// 计算文本绘制的位置
CGSize stringSize = ;
CGPoint stringPoint = CGPointMake(CGRectGetMidX(rect) - stringSize.width / 2,
CGRectGetMidY(rect) - stringSize.height / 2);
// 绘制文本
;
}
</code></pre>
<p>这段代码先绘制了一个实心圆形,然后在其上居中绘制了一段文字。通过计算文本尺寸和视图的中点来确定文本的绘制位置,使文本能够恰当地居中显示。</p>
<hr>
<h3 id="16高级路径绘制和图形变换">16.高级路径绘制和图形变换</h3>
<p>我们将展示如何绘制一个复杂的图形,并对其进行变换。这个例子会使用到路径创建、填充、描边、以及使用仿射变换对图形进行平移和旋转。</p>
<h4 id="示例代码-12">示例代码</h4>
<p>我们将创建一个自定义的UIView,它会绘制一个星形图案,然后对星形应用旋转和平移变换。</p>
<pre><code class="language-objective-c">// StarView.h
#import <UIKit/UIKit.h>
@interface StarView : UIView
@end
</code></pre>
<pre><code class="language-objective-c">// 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, .CGColor);
CGContextFillPath(ctx);
CGContextAddPath(ctx, transformedPath);
CGContextSetStrokeColorWithColor(ctx, .CGColor);
CGContextStrokePath(ctx);
CGPathRelease(starPath);
CGPathRelease(transformedPath);
}
@end
</code></pre>
<p>该示例中,首先创建了一个五角星形的路径<code>starPath</code>,然后通过<code>CGAffineTransform</code>创建了一个变换,将星形下移并旋转。使用<code>CGPathCreateCopyByTransformingPath()</code>函数应用这个变换到路径上,并通过Core Graphics的上下文将其绘制到视图上。</p>
<h3 id="使用图层蒙版和阴影效果">使用图层蒙版和阴影效果</h3>
<p>通过结合使用Core Graphics和Core Animation,我们可以实现一些高级的渲染效果,如蒙版和阴影。</p>
<h4 id="示例代码-13">示例代码</h4>
<p>这个示例将向你展示如何给一个UIView的子类添加阴影效果,并使用图层蒙版来显示一部分内容。</p>
<pre><code class="language-objective-c">- (void)setupShadowAndMask {
CALayer *layer = self.layer;
layer.shadowOpacity = 0.5;
layer.shadowRadius = 5.0;
layer.shadowOffset = CGSizeMake(5.0, 5.0);
layer.shadowColor = .CGColor;
// 创建一个圆形蒙版
CAShapeLayer *maskLayer = ;
CGPathRef circlePath = CGPathCreateWithEllipseInRect(self.bounds, NULL);
maskLayer.path = circlePath;
layer.mask = maskLayer;
CGPathRelease(circlePath);
}
</code></pre>
<p>在这个示例中,首先对视图的根层(<code>CALayer</code>)设置了阴影效果,包括阴影的透明度、半径、偏移量和颜色。然后创建了一个<code>CAShapeLayer</code>图层作为蒙版,使用一个椭圆路径来限制只在这个椭圆路径范围内显示内容。最后,将这个蒙版图层设置给根层的<code>mask</code>属性。</p>
<hr>
<p>理解了基本的绘制技巧和高级效果之后,让我们尝试一个更富有挑战性、也更加“吊炸天”的使用场景:创建一个动态波浪效果的视图,这个视图可以用作动态背景,展示Loading状态,或者在音乐应用中表示音量变化等。</p>
<h3 id="17动态波浪效果">17.动态波浪效果</h3>
<p>我们将创建一个自定义的<code>UIView</code>,内部使用<code>CADisplayLink</code>来实现一个连续动态的波浪效果。这个波浪效果使用正弦函数生成,可通过调整相关参数来模拟水波的动态。以下是实现这一效果的步骤及相关代码。</p>
<h4 id="定义自定义视图">定义自定义视图</h4>
<p>首先,定义一个UIView的子类<code>WaveView</code>来封装波浪效果的绘制逻辑。</p>
<pre><code class="language-objective-c">// WaveView.h
#import <UIKit/UIKit.h>
@interface WaveView : UIView
@property (nonatomic, strong) UIColor *waveColor; // 波浪颜色
- (void)startWave;
- (void)stopWave;
@end
</code></pre>
<p>在<code>WaveView</code>的实现中,我们使用<code>CADisplayLink</code>来周期性更新波浪的位置,实现动态效果。</p>
<pre><code class="language-objective-c">// 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 = ) {
self.waveColor = ;
self.offset = 0;
}
return self;
}
- (void)startWave {
self.displayLink = ;
forMode:NSRunLoopCommonModes];
}
- (void)stopWave {
;
self.displayLink = nil;
}
- (void)wave {
// 更新波浪的水平偏移
self.offset += 10;
// 重绘
;
}
- (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
</code></pre>
<p>在上述代码中,<code>startWave</code>方法会启动一个<code>CADisplayLink</code>实例周期性调用<code>wave</code>方法,后者通过增加水平偏移<code>self.offset</code>来动态更新波浪路径,并请求重新绘制UIView。<code>drawRect:</code>方法会根据当前的水平偏移计算每个点的Y坐标,绘制出动态变化的波浪效果。</p>
<h3 id="使用">使用</h3>
<p>你可以在需要动态波浪效果的地方,创建<code>WaveView</code>的实例,并调用<code>startWave</code>方法开始动画,使用<code>stopWave</code>方法停止动画。</p>
<pre><code class="language-objective-c">WaveView *waveView = [ initWithFrame:CGRectMake(0, 0, 300, 200)];
;
;
</code></pre>
<h3 id="小结">小结</h3>
<p>这个示例演示了如何结合使用Core Graphics和<code>CADisplayLink</code>来创建一个动态且吸引人的视觉效果。通过调整波浪动态的参数,你可以创建出多种不同风格的波浪效果,为你的应用提供丰富且美观的视觉展现。</p>
</div>
<div id="MySignature" role="contentinfo">
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。<br><br>
来源:https://www.cnblogs.com/chglog/p/18307049
頁:
[1]