1 绘制基本图形
1.1 问题
IOS中进行绘制比较方便,只需要在视图类(UIView及其子类)中重写drawRect方法,将绘制代码要写在该方法中即可,此方法会在视图显示前自动调用。本案例重写视图类中的drawRect方法,绘制一个简单的图形,如图-1所示:
图-1
1.2 方案
首先在创建好的Xcode项目中创建一个TRMyView类,继承至UIView。在Storyboard中拖放一个View控件,在右边栏的检查器中将View和TRMyView类进行绑定,并将背景设置为紫色。
然后在TRMyView中重写drawRect:方法,在该方法中绘制三条直线,连接成一个三角形。
1.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建TRMyView类
首先在创建好的Xcode项目中创建一个TRMyView类,继承至UIView。在Storyboard中拖放一个View将控件,在右边栏的检查器中将View和TRMyView类进行绑定,并将背景设置为紫色,如图-2所示:
图-2
步骤二:重写drawRect方法进行屏幕绘制
在TRMyView中重写drawRect:方法,首先在该方法中通过UIGraphicsGetCurrentContext函数获取上下文即画板,再通过CGContextSaveGState函数保存上下文,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext(); //获取当前上下文,其实是内存中的一片空间
- CGContextSaveGState(context);
- }
其次绘制三条直线,勾勒出三角形的轮廓,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- //将画笔移动到某一点
- CGContextMoveToPoint(context, 20, 20);
- //在两点之间画一条直线
- CGContextAddLineToPoint(context, 20, 120);
- CGContextAddLineToPoint(context, 100, 20);
- CGContextAddLineToPoint(context, 20, 20);
- }
然后配置颜色,将描边颜色设置为绿色,填充颜色设置为红色,使用CGContextDrawPath函数进行描边和填充,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- CGContextMoveToPoint(context, 20, 20);
- CGContextAddLineToPoint(context, 20, 120);
- CGContextAddLineToPoint(context, 100, 20);
- CGContextAddLineToPoint(context, 20, 20);
- // 调色
- CGContextSetStrokeColorWithColor(context, [[UIColor greenColor]CGColor]);
- CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
- //上色
- CGContextDrawPath(context, kCGPathFillStroke);
- }
最后保存上下文完成绘制,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- CGContextMoveToPoint(context, 20, 20);
- CGContextAddLineToPoint(context, 20, 120);
- CGContextAddLineToPoint(context, 100, 20);
- CGContextAddLineToPoint(context, 20, 20);
- CGContextSetStrokeColorWithColor(context, [[UIColor greenColor]CGColor]);
- CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
- CGContextDrawPath(context, kCGPathFillStroke);
- //保存上下文
- CGContextRestoreGState(context);
- }
1.4 完整代码
本案例中,TRMyView.m文件中的完整代码如下所示:
- #import "TRMyView.h"
- @implementation TRMyView
- - (void)drawRect:(CGRect)rect
- {
- //1. 拿一个画板,获取当前上下文,其实是内存中的一片空间
- CGContextRef context = UIGraphicsGetCurrentContext();
- //1.5 保存上下文状态
- CGContextSaveGState(context);
- //2.勾勒一个轮廓
- CGContextMoveToPoint(context, 20, 20);
- CGContextAddLineToPoint(context, 20, 120);
- CGContextAddLineToPoint(context, 100, 20);
- CGContextAddLineToPoint(context, 20, 20);
- //3. 调色
- CGContextSetStrokeColorWithColor(context, [[UIColor greenColor]CGColor]);
- CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
- //4. 上色
- CGContextDrawPath(context, kCGPathFillStroke);
- //5. 恢复上下文状态
- CGContextRestoreGState(context);
- }
- @end
2 绘制简单的贝塞尔路径
2.1 问题
UIBezierPath对底层Core Graphics的一部分常见的绘制API的封装类,能按照面向对象的方式绘制图形,例如直线、曲线、圆形、扇形、多边型等。本案例重写drawRect方法,绘制一个简单的贝塞尔路径,如图-3所示:
图-4
2.2 方案
首先在创建好的Xcode项目中创建一个TRMyView类,继承至UIView。在Storyboard中拖放一个和屏幕一样大小的View控件,在右边栏的检查器中将View和TRMyView类进行绑定。
然后在TRMyView中重写drawRect:方法,在该方法中创建一个UIBezierPath类型的对象path,通过path绘制一个三角形。
2.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建TRMyView类
首先在创建好的Xcode项目中创建一个TRMyView类,继承至UIView。在Storyboard中拖放一个和屏幕一样大小的View控件,在右边栏的检查器中将View和TRMyView类进行绑定,如图-5所示:
图-5
步骤二:重写drawRect方法使用UIBezierPath绘制图形
在TRMyView中重写drawRect:方法,同样先在该方法中通过UIGraphicsGetCurrentContext函数获取上下文,再通过CGContextSaveGState函数保存上下文,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- }
然后创建一个UIBezierPath类型的对象path,通过path勾勒出图形,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- UIBezierPath *path = [UIBezierPath bezierPath];
- [path moveToPoint:CGPointMake(20, 20)];
- [path addLineToPoint:CGPointMake(20, self.frame.size.height - 40)];
- [path addLineToPoint:CGPointMake(self.frame.size.width - 40, 20)];
- }
再进行颜色配置将描边颜色设置为红色,填充颜色设置为绿色,并设置线头和连接处的样式,通过path进行描边和填充,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- UIBezierPath *path = [UIBezierPath bezierPath];
- [path moveToPoint:CGPointMake(20, 20)];
- [path addLineToPoint:CGPointMake(20, self.frame.size.height - 40)];
- [path addLineToPoint:CGPointMake(self.frame.size.width - 40, 20)];
- //封闭路径
- [path closePath];
- //配置颜色
- [[UIColor redColor] setStroke];
- [[UIColor greenColor] setFill];
- //线宽
- path.lineWidth = 10;
- //连接处的风格
- path.lineJoinStyle = kCGLineJoinBevel;
- //线头的风格
- path.lineCapStyle = kCGLineCapSquare;
- //填充和描边
- [path fill];
- [path stroke];
- }
最后保存上下文完成绘制,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- UIBezierPath *path = [UIBezierPath bezierPath];
- [path moveToPoint:CGPointMake(20, 20)];
- [path addLineToPoint:CGPointMake(20, self.frame.size.height - 40)];
- [path addLineToPoint:CGPointMake(self.frame.size.width - 40, 20)];
- //封闭路径
- [path closePath];
- //配置颜色
- [[UIColor redColor] setStroke];
- [[UIColor greenColor] setFill];
- //线宽
- path.lineWidth = 10;
- //连接处的风格
- path.lineJoinStyle = kCGLineJoinBevel;
- //线头的风格
- path.lineCapStyle = kCGLineCapSquare;
- //填充和描边
- [path fill];
- [path stroke];
- }
2.4 完整代码
本案例中,TRMyView.m文件中的完整代码如下所示:
- #import "TRMyView.h"
- @implementation TRMyView
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- UIBezierPath *path = [UIBezierPath bezierPath];
- [path moveToPoint:CGPointMake(20, 20)];
- [path addLineToPoint:CGPointMake(20, self.frame.size.height - 40)];
- [path addLineToPoint:CGPointMake(self.frame.size.width - 40, 20)];
- //封闭路径
- [path closePath];
- //配置颜色
- [[UIColor redColor] setStroke];
- [[UIColor greenColor] setFill];
- //线宽
- path.lineWidth = 10;
- //连接处的风格
- path.lineJoinStyle = kCGLineJoinBevel;
- //线头的风格
- path.lineCapStyle = kCGLineCapSquare;
- //填充和描边
- [path fill];
- [path stroke];
- CGContextRestoreGState(context);
- }
- @end
3 使用UIBezierPath画一个扇形
3.1 问题
本案例在上一个案例的基础上使用UIBezierPath绘制一个扇形,如图-6所示:
图-6
3.2 方案
本案例直接在上一个案例的基础上实现,首先在drawRect:方法中使用UIBezierPath的工厂方法bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:创建一个弧形对象,arcCenter参数表示弧形的中心点是一个CGPoint类型的数据,radius参数是弧形的半径长度,startAngle参数是开始弧度,endAngle参数是结束弧度,clockwise是一个BOOL类型表示弧度的方向,是否为顺时针。
然后设置扇形的描边颜色和填充颜色,进行描边和填充。
3.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建弧形对象
首先在上一个案例中的drawRect:方法中使用UIBezierPath的工厂方法bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:创建一个弧形对象,arcCenter参数表示弧形的中心点是一个CGPoint类型的数据,radius参数是弧形的半径长度,startAngle参数是开始弧度,endAngle参数是结束弧度,clockwise是一个BOOL类型表示弧度的方向,是否为顺时针,代码如下所示:
- UIBezierPath *myArc = [UIBezierPath bezierPathWithArcCenter:CGPointMake(200, 350) radius:100 startAngle:M_PI_2 * 3 endAngle:M_PI clockwise:YES];
然后从弧线的结束点到弧线的中心画一条直线,并将路径封闭即完成了扇形的勾勒,代码如下所示:
- [myArc addLineToPoint:CGPointMake(200, 350)];
- [myArc closePath];
步骤二:设置扇形的属性
首先配置颜色,分别设置描边颜色为红色,填充颜色为蓝色,线宽为4,代码如下所示:
- [[UIColor redColor]setStroke];
- [[UIColor blueColor]setFill];
- myArc.lineWidth = 4;
最后描边和填充,注意以上所有代码都要写在保存上下文代码的前面,代码如下所示:
- [myArc fill];
- [myArc stroke];
- CGContextRestoreGState(context);
3.4 完整代码
本案例中,TRViewController.m文件中的完整代码如下所示:
- #import "TRMyView.h"
- @implementation TRMyView
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- UIBezierPath *path = [UIBezierPath bezierPath];
- [path moveToPoint:CGPointMake(20, 20)];
- [path addLineToPoint:CGPointMake(20, self.frame.size.height - 40)];
- [path addLineToPoint:CGPointMake(self.frame.size.width - 40, 20)];
- //封闭路径
- [path closePath];
- //配置颜色
- [[UIColor redColor] setStroke];
- [[UIColor greenColor] setFill];
- //线宽
- path.lineWidth = 10;
- //连接处的风格
- path.lineJoinStyle = kCGLineJoinBevel;
- //线头的风格
- path.lineCapStyle = kCGLineCapSquare;
- //填充和描边
- [path fill];
- [path stroke];
- //弧线 扇形 圆
- UIBezierPath *myArc = [UIBezierPath bezierPathWithArcCenter:CGPointMake(200, 350) radius:100 startAngle:M_PI_2 * 3 endAngle:M_PI clockwise:YES];
- [myArc addLineToPoint:CGPointMake(200, 350)];
- [myArc closePath];
- [[UIColor redColor]setStroke];
- [[UIColor blueColor]setFill];
- myArc.lineWidth = 4;
- [myArc fill];
- [myArc stroke];
- CGContextRestoreGState(context);
- }
- @end
4 使用UIBezierPath画一个下载标记
4.1 问题
本案例结合UISlider控件模拟网络下载进度,绘制一个等待下载的图标,如图-7所示:
图-7
4.2 方案
首先在创建好的Xcode项目中,创建一个TRDownloadIndicator类继承至UIView,该类用于绘制下载图标,它有两个属性一个是UIColor类型的color,用于记录绘制的颜色,另一个是float类型的value,用于记录下载进度。
其次在Storyboard的场景上方拖放一个Slider控件,然后在Slider控件的下方拖放一个View控件,将View控件和TRDownloadIndicator类进行绑定。并将Slider控件关联成TRViewController的方法sliderValueChanged:,View关联成TRViewController的输出口属性downloadingIndicator。
然后在TRDownloadIndicator类中重写drawRect:方法进行下载图标的绘制,即绘制一个空心的圆弧,但是圆弧的弧长由value属性决定。当value值改变则需要重新绘制,可以重写value的setter方法,在setter方法中调用setNeedDisplay方法进行重新绘制。
最后在TRViewController类中实现sliderValueChanged:方法,该方法中downloadingIndicator的value属性根据slider控件的value值而改变。
4.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建TRDownloadIndicator类
首先在创建好的Xcode项目中,创建一个TRDownloadIndicator类继承至UIView,该类用于绘制下载图标,它有两个属性一个是UIColor类型的color,用于记录绘制的颜色,另一个是float类型的value,用于记录下载进度,代码如下所示:
- @interface TRDownloadIndicator : UIView
- @property (strong, nonatomic) UIColor *color;
- @property (nonatomic) CGFloat value;
- @end
- //重写color的getter方法,将初始颜色设置为蓝色
- - (UIColor *)color
- {
- if (!_color) {
- _color = [UIColor blueColor];
- }
- return _color;
- }
步骤二:Storyboard中搭建界面
首先在Storyboard的场景上方拖放一个Slider控件,然后在Slider控件的下方拖放一个View控件,将View控件和TRDownloadIndicator类进行绑定,如图-8所示:
图-8
最后将Slider控件关联成TRViewController的方法sliderValueChanged:,View关联成TRViewController的输出口属性downloadingIndicator,代码如下所示:
- //关联downloadingIndicator属性
- @interface TRViewController ()
- @property (weak, nonatomic) IBOutlet TRDownloadIndicator *downloadingIndicator;
- @end
- //关联slider ValueChanged:(方法
- - (IBAction)sliderValueChanged:(UISlider *)slider
- {
- }
步骤三:重写drawRect方法,进行绘制
首先在TRDownloadIndicator类中重写drawRect:方法进行下载图标的绘制,即使用UIBezierPath绘制一个空心的圆弧,圆弧的弧长由value属性决定,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGPoint center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
- CGFloat lineWidth = self.bounds.size.width / 10;
- CGFloat radius = self.bounds.size.width / 2 - lineWidth;
- UIBezierPath *path = [UIBezierPathbezierPathWithArcCenter:centerradius:radiusstartAngle:M_PI_2 * 3endAngle: self.value * M_PI *2 + M_PI_2 * 3clockwise:YES];
- path.lineWidth = lineWidth;
- [self.color setStroke];
- [path stroke];
- }
当value值改变则需要重新绘制,可以重写value的setter方法,在setter方法中调用setNeedDisplay方法进行重新绘制,代码如下所示:
- - (void)setValue:(CGFloat)value
- {
- _value = value;
- //重绘当前视图
- [self setNeedsDisplay];
- }
步骤四:实现sliderChangeValue:方法
我们可以在TRViewController的viewDidLoad方法中进行一些初始设置,例如downloadingIndicator的color属性和背景颜色,代码如下所示:
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- self.downloadingIndicator.color = [UIColor whiteColor];
- self.downloadingIndicator.backgroundColor = [UIColor grayColor];
- }
最后在TRViewController类中实现sliderValueChanged:方法,该方法中downloadingIndicator的value属性根据slider控件的value值而改变,当downloadingIndicator的value值改变就会重新进行屏幕绘制,代码如下所示:
- - (IBAction)sliderValueChanged:(UISlider *)slider
- {
- self.downloadingIndicator.value = slider.value;
- }
运行程序,下载标记就会根据slider滑块的滑动进行绘制。
4.4 完整代码
本案例中,TRViewController.m文件中的完整代码如下所示:
- #import "TRViewController.h"
- #import "TRDownloadIndicator.h"
- @interface TRViewController ()
- @property (weak, nonatomic) IBOutlet TRDownloadIndicator *downloadingIndicator;
- @end
- @implementation TRViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- self.downloadingIndicator.color = [UIColor whiteColor];
- self.downloadingIndicator.backgroundColor = [UIColor grayColor];
- }
- - (IBAction)sliderValueChanged:(UISlider *)slider
- {
- self.downloadingIndicator.value = slider.value;
- }
- @end
本案例中,TRDownloadIndicator.h文件中的完整代码如下所示:
- #import <UIKit/UIKit.h>
- @interface TRDownloadIndicator : UIView
- @property (strong, nonatomic) UIColor *color;
- @property (nonatomic) CGFloat value;
- @end
本案例中,TRDownloadIndicator.m文件中的完整代码如下所示:
- #import "TRDownloadIndicator.h"
- @implementation TRDownloadIndicator
- - (UIColor *)color
- {
- if (!_color) {
- _color = [UIColor blueColor];
- }
- return _color;
- }
- - (void)setValue:(CGFloat)value
- {
- _value = value;
- //重绘当前视图
- [self setNeedsDisplay];
- }
- - (void)drawRect:(CGRect)rect
- {
- CGPoint center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
- CGFloat lineWidth = self.bounds.size.width / 10;
- CGFloat radius = self.bounds.size.width / 2 - lineWidth;
- UIBezierPath *path =[UIBezierPathbezierPathWithArcCenter:centerradius:radiusstartAngle:M_PI_2 * 3endAngle: self.value * M_PI *2 + M_PI_2 * 3clockwise:YES];
- path.lineWidth = lineWidth;
- [self.color setStroke];
- [path stroke];
- }
- @end
5 使用UIBezierPath画其他图形
5.1 问题
UIBezierPath除了可以绘制直线和圆弧还有绘制矩形、圆角矩形、椭圆以及贝塞尔曲线等。本案例使用UIBezierPath绘制以上各个图形,如图-9所示:
图-9
5.2 方案
首先同之前的案例一样在创建好的Xcode项目中创建一个TRBezierPathView类,继承至UIView。在Storyboard中拖放一个和屏幕一样大小的View控件,在右边栏的检查器中将View和TRBezierPathView类进行绑定。
然后在TRBezierPathView中重写drawRect:方法,在该方法中首先获取上下文状态。然后中依次使用如下UIBezierPath的绘制方法绘制矩形、圆角矩形、椭圆形以及贝塞尔曲线:
绘制矩形使用工厂方法bezierPathWithRect:;
绘制圆角矩形使用工厂方法bezierPathWithRoundedRect:cornerRadius:;
绘制椭圆形使用工厂方法bezierPathWithOvalInRect:;
绘制贝塞尔曲线使用方法addCurveToPoint:controlPoint1:controlPoint2:。
最后恢复上下文状态。
5.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建TRBezierPathView类
首先同之前的案例一样在创建好的Xcode项目中创建一个TRBezierPathView类,继承至UIView。在Storyboard中拖放一个和屏幕一样大小的View控件,在右边栏的检查器中将View和TRBezierPathView类进行绑定,如图-10所示:
图-10
步骤二:重写drawRect方法使用UIBezierPath绘制不同的图形
在TRBezierPathView中重写drawRect:方法,在该方法中首先获取上下文状态,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- }
其次绘制一个矩形,使用工厂方法bezierPathWithRect:创建一个UIBezierPath类型的对象myRect,rect:是一个CGRect类型的参数表示矩形的原点左边和宽高,然后给矩形进行颜色配置和描边,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- UIBezierPath *myRect = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, self.bounds.size.width - 40, self.bounds.size.height - 40)];
- myRect.lineWidth = 4;
- [[UIColor lightGrayColor] setStroke];
- [myRect stroke];
- }
再绘制一个圆角矩形,使用工厂方法bezierPathWithRoundedRect: cornerRadius:创建一个UIBezierPath类型的对象myRoundRect,其中rect参数是一个CGRect类型的参数表示矩形的原点左边和宽高,cornerRadius参数表示圆角的半径,然后对圆角矩形进行颜色配置和描边,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- //绘制矩形
- UIBezierPath *myRect = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, self.bounds.size.width - 40, self.bounds.size.height - 40)];
- myRect.lineWidth = 4;
- [[UIColor lightGrayColor] setStroke];
- [myRect stroke];
- //绘制圆角矩形
- UIBezierPath *myRoundRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(40, 40, 100, 80) cornerRadius:10];
- myRoundRect.lineWidth = 4;
- [[UIColor purpleColor] setStroke];
- [myRoundRect stroke];
- }
然后绘制一个椭圆形,使用工厂方法bezierPathWithOvalInRect:创建一个UIBezierPath类型的对象myOval,该方法创建的是一个矩形的内切椭圆,其中rect参数就是这个矩形的原点左边和宽高,然后对椭圆进行颜色配置和描边,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- //绘制矩形
- UIBezierPath *myRect = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, self.bounds.size.width - 40, self.bounds.size.height - 40)];
- myRect.lineWidth = 4;
- [[UIColor lightGrayColor] setStroke];
- [myRect stroke];
- //绘制圆角矩形
- UIBezierPath *myRoundRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(40, 40, 100, 80) cornerRadius:10];
- myRoundRect.lineWidth = 4;
- [[UIColor purpleColor] setStroke];
- [myRoundRect stroke];
- //绘制椭圆
- UIBezierPath *myOval = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(170, 40, 100, 50)];
- myOval.lineWidth = 4;
- [[UIColor blueColor]setStroke];
- [myOval stroke];
- }
最后绘制一个贝塞尔曲线并恢复上下文,使用方法bezierPath创建一个UIBezierPath类型的对象myCurve,myCurve通过方法moveToPoint和方法addCurveToPoint:controlPoint1:controlPoint2:勾勒出一条贝塞尔曲线的路径,一条贝塞尔曲线有一个开始点和一个结束点,还有两个控制点,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- //绘制矩形
- UIBezierPath *myRect = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, self.bounds.size.width - 40, self.bounds.size.height - 40)];
- myRect.lineWidth = 4;
- [[UIColor lightGrayColor] setStroke];
- [myRect stroke];
- //绘制圆角矩形
- UIBezierPath *myRoundRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(40, 40, 100, 80) cornerRadius:10];
- myRoundRect.lineWidth = 4;
- [[UIColor purpleColor] setStroke];
- [myRoundRect stroke];
- //绘制椭圆
- UIBezierPath *myOval = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(170, 40, 100, 50)];
- myOval.lineWidth = 4;
- [[UIColor blueColor]setStroke];
- [myOval stroke];
- //绘制贝塞尔曲线
- UIBezierPath *myCurve = [UIBezierPath bezierPath];
- [myCurve moveToPoint:CGPointMake(80, 220)];
- [myCurve addCurveToPoint:CGPointMake(40, 320) controlPoint1:CGPointMake(40, 220) controlPoint2:CGPointMake(80, 320)];
- [myCurve addCurveToPoint:CGPointMake(80, 420) controlPoint1:CGPointMake(80, 320) controlPoint2:CGPointMake(40, 420)];
- myCurve.lineWidth = 4;
- [[UIColor orangeColor]setStroke];
- [myCurve stroke];
- CGContextRestoreGState(context);
- }
5.4 完整代码
本案例中,TRBezierPathView.m文件中的完整代码如下所示:
- #import "TRBezierPathView.h"
- @implementation TRBezierPathView
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
- //矩形
- UIBezierPath *myRect = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, self.bounds.size.width - 40, self.bounds.size.height - 40)];
- myRect.lineWidth = 4;
- [[UIColor lightGrayColor] setStroke];
- [myRect stroke];
- //圆角矩形
- UIBezierPath *myRoundRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(40, 40, 100, 80) cornerRadius:10];
- myRoundRect.lineWidth = 4;
- [[UIColor purpleColor] setStroke];
- [myRoundRect stroke];
- //椭圆
- UIBezierPath *myOval = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(170, 40, 100, 50)];
- myOval.lineWidth = 4;
- [[UIColor blueColor]setStroke];
- [myOval stroke];
- //曲线 贝塞尔曲线
- UIBezierPath *myCurve = [UIBezierPath bezierPath];
- [myCurve moveToPoint:CGPointMake(80, 220)];
- [myCurve addCurveToPoint:CGPointMake(40, 320) controlPoint1:CGPointMake(40, 220) controlPoint2:CGPointMake(80, 320)];
- [myCurve addCurveToPoint:CGPointMake(80, 420) controlPoint1:CGPointMake(80, 320) controlPoint2:CGPointMake(40, 420)];
- myCurve.lineWidth = 4;
- [[UIColor orangeColor]setStroke];
- [myCurve stroke];
- CGContextRestoreGState(context);
- }
- @end
6 绘制字符串
6.1 问题
除了可以在屏幕上绘制各种图形还可以绘制字符串,本案例使用NSString提供的方法在屏幕上绘制字符串,如图-11,图-12所示:
图-11
图-12
6.2 方案
首先同之前的案例一样在创建好的Xcode项目中创建一个TRStringView类,继承至UIView。在Storyboard中拖放一个和屏幕一样大小的View控件,在右边栏的检查器中将View和TRStringView类进行绑定。
然后在TRStringView中重写drawRect:方法,在该方法中首先创建一个NSString类型的字符串,然后使用NSString提供的绘制字符串的方法进行屏幕绘制,本案例将使用如下四种字符串的绘制方法进行绘制:
drawAtPoint: withFont:;
drawInRect:withFont:;
drawAtPoint:withAttributes:;
drawInRect:withAttributes:。
6.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建TRStringView类
首先同之前的案例一样在创建好的Xcode项目中创建一个TRStringView类,继承至UIView。在Storyboard中拖放一个和屏幕一样大小的View控件,在右边栏的检查器中将View和TRStringView类进行绑定,如图-13所示:
图-13
步骤二:重写drawRect方法绘制字符串
在TRView中重写drawRect:方法,在该方法中首先创建一个NSString类型的字符串message,然后使用NSString提供的绘制方法drawAtPoint:withFont:绘制字符串,point是字符串绘制在屏幕上的原点坐标,font参数是字符串的字体,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- NSString *message = @"Hello World.";
- [message drawAtPoint:CGPointMake(40, 40) withFont:[UIFont systemFontOfSize:30]];
- }
其次使用NSString提供的绘制方法drawAtRect:withFont:绘制字符串,rect参数是字符串绘制在屏幕上的原点坐标和范围可以实现字符串的自动换行,font参数是字符串的字体,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- NSString *message = @"Hello World.";
- [message drawAtPoint:CGPointMake(40, 40) withFont:[UIFont systemFontOfSize:30]];
- message = @"I am a loooooooooooooooooooooooooooooooong string";
- [message drawInRect:CGRectMake(20, 80, 280, 200) withFont:[UIFont italicSystemFontOfSize:30]];
- }
如果无法确定字符串绘制在屏幕上的范围,可以使用方法sizeWithFont: constrainedToSize:根据字符串的字体大小和宽度计算出字符串的范围,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- NSString *message = @"Hello World.";
- [message drawAtPoint:CGPointMake(40, 40) withFont:[UIFont systemFontOfSize:30]];
- message = @"I am a loooooooooooooooooooooooooooooooong string";
- [message drawInRect:CGRectMake(20, 80, 280, 200) withFont:[UIFont italicSystemFontOfSize:30]];
- message = @"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ";
- CGSize size = [message sizeWithFont:[UIFont systemFontOfSize:24] constrainedToSize:CGSizeMake(280, 999)];
- NSLog(@"size:(%.2f, %.2f)", size.width, size.height);
- [message drawInRect:CGRectMake(20, 300, size.width, size.height) withFont:[UIFont systemFontOfSize:24]];
- }
步骤三:绘制属性字符串
通过以上两种方法绘制字符串发现并不能进行更多属性的设置,例如字符串的颜色和背景,所以我们还可以使用另外两种字符串的绘制方法drawAtPoint:withAttributes和drawInRect:withAttributes:,atrributes是一个NSDictionary类型的参数,通过该参数可以设置更多字符串的的属性,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- NSString *message = @"Hello World.";
- NSDictionary *attributes = @{NSFontAttributeName : [UIFont italicSystemFontOfSize:34], NSForegroundColorAttributeName : [UIColor redColor], NSBackgroundColorAttributeName : [UIColor lightGrayColor]};
- [message drawAtPoint:CGPointMake(40, 40) withAttributes:attributes];
- message = @"This is a long long long long long long string.";
- attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:24], NSForegroundColorAttributeName : [UIColor purpleColor]};
- [message drawInRect:CGRectMake(20, 80, 280, 100) withAttributes:attributes];
- message = @"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ";
- CGRect r = [message boundingRectWithSize:CGSizeMake(280, 999) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
- r.origin = CGPointMake(20, 300);
- [message drawInRect:r withAttributes:attributes];
- }
6.4 完整代码
本案例中,TRStringView.m文件中的完整代码如下所示:
- #import "TRStringView.h"
- @implementation TRStringView
- - (void)drawRect:(CGRect)rect
- {
- // NSString *message = @"Hello World.";
- // [message drawAtPoint:CGPointMake(40, 40) withFont:[UIFont systemFontOfSize:30]];
- // message = @"I am a loooooooooooooooooooooooooooooooong string";
- // [message drawInRect:CGRectMake(20, 80, 280, 200) withFont:[UIFont italicSystemFontOfSize:30]];
- // message = @"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ";
- // CGSize size = [message sizeWithFont:[UIFont systemFontOfSize:24] constrainedToSize:CGSizeMake(280, 999)];
- // NSLog(@"size:(%.2f, %.2f)", size.width, size.height);
- // [message drawInRect:CGRectMake(20, 300, size.width, size.height) withFont:[UIFont systemFontOfSize:24]];
- NSString *message = @"Hello World.";
- NSDictionary *attributes = @{NSFontAttributeName : [UIFont italicSystemFontOfSize:34], NSForegroundColorAttributeName : [UIColor redColor], NSBackgroundColorAttributeName : [UIColor lightGrayColor]};
- [message drawAtPoint:CGPointMake(40, 40) withAttributes:attributes];
- message = @"This is a long long long long long long string.";
- attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:24], NSForegroundColorAttributeName : [UIColor purpleColor]};
- [message drawInRect:CGRectMake(20, 80, 280, 100) withAttributes:attributes];
- message = @"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ";
- CGRect r = [message boundingRectWithSize:CGSizeMake(280, 999) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
- r.origin = CGPointMake(20, 300);
- [message drawInRect:r withAttributes:attributes];
- }
- @end
7 绘制图片
7.1 问题
除了可以在屏幕上绘制各种图形、字符串还可以绘制图片image,本案例使用UIImage提供的方法在屏幕上绘制图片,如图-14、图-15所示:
图-14
图-15
7.2 方案
首先在创建好的Xcode项目中创建一个TRImageView类,继承至UIView。该类有一个UIImage类型的属性image,用于记录需要绘制的图片。在Storyboard中拖放一个适当大小的View控件,放置在屏幕中间,在右边栏的检查器中将View和TRImageView类进行绑定,并将View关联成TRViewController的属性myImageView。
然后在TRImageView中重写drawRect:方法,在该方法中首先创建一个UIBezierPath类型的对象path,通过path勾勒出一个圆角矩形的图形,然后通过addClip方法根据path勾勒的路径剪贴绘制上下文,最后使用UIImage的drawInRect方法绘制图片,在image属性的setter方法中进行重新绘制。
最后在TRViewController的viewDidLoad方法中设置myImageView的图片image。
除了可以通过重写drawRect:方法进行图片绘制,还可以在视图控制器中通过创建画板的方式进行图片的绘制,接下来新创建一个场景用于演示如何使用创建画板的方式绘制图片,该场景和TRImageViewController类进行绑定。
首先将本案例的场景嵌套一个NavigationController,在导航栏上拖拽一个BarButtonItem按钮,点击按钮跳转到一个新的场景。
然后在TRImageViewController的viewDidLoad方法中通过UIGraphicsBeginImageContext函数获取一个画板,然后在画板上绘制图片,并通过UIGraphicsGetImageFromCurrentImageContext函数从画板上将图片扣下来,获取到一个UIImage类型的对象newImage。
最后创建一个UIImageView对象imageView用于显示绘制完成的图片,即将imageView的image属性设置为newImage,并添加到父视图中。
7.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建TRImageView类
首先在创建好的Xcode项目中创建一个TRImageView类,继承至UIView。该类有一个UIImage类型的属性image,用于记录需要绘制的图片,代码如下所示:
- @interface TRImageView : UIView
- @property (strong, nonatomic) UIImage *image;
- @end
然后在Storyboard中拖放一个适当大小的View控件,放置在屏幕中间,在右边栏的检查器中将View和TRImageView类进行绑定,并将View关联成TRViewController的属性myImageView,代码如下所示:
- @interface TRViewController ()
- @property (weak, nonatomic) IBOutlet TRImageView *myImageView;
- @end
步骤二:重写drawRect方法绘制图片
在TRImageView中重写drawRect:方法,首先在该方法中首先创建一个UIBezierPath类型的对象path,通过path勾勒出一个圆角矩形的图形,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.bounds.size.width / 10];
- }
然后通过addClip方法根据path勾勒的路径剪贴绘制上下文,再使用UIImage的drawInRect方法绘制图片,代码如下所示:
- - (void)drawRect:(CGRect)rect
- {
- UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.bounds.size.width / 10];
- //按照路径剪贴绘制上下文
- [path addClip];
- //在指定的位置和空间绘制图像
- [self.image drawInRect:self.bounds];
- }
在TRImageView.m文件中重写image属性的setter方法,在该方法中进行重新绘制,代码如下所示:
- - (void)setImage:(UIImage *)image
- {
- _image = image;
- [self setNeedsDisplay];
- }
最后在TRViewController的viewDidLoad方法中设置myImageView的图片,代码如下所示:
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- self.myImageView.image = [UIImage imageNamed:@"6.jpg"];
- }
步骤三:在视图控制器中绘制图片
首先将本案例的场景嵌套一个NavigationController,在导航栏上拖拽一个BarButtonItem按钮,点击按钮跳转到一个新的场景,新场景和新建的TRImageViewController类进行绑定,如图-16所示:
图-16
其次在TRImageViewController的viewDidLoad方法中通过UIGraphicsBeginImageContext函数获取一个画板,然后在画板上进行图片的绘制,代码如下所示:
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- CGRect rect = CGRectMake(0, 0, 240, 300);
- //创建画板(上下文)
- UIGraphicsBeginImageContext(rect.size);
- //对画板进行裁剪
- UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
- [path addClip];
- //要画的图片
- UIImage *image = [UIImage imageNamed:@"7.jpg"];
- //在画板上画
- [image drawInRect:rect];
- }
然后通过UIGraphicsGetImageFromCurrentImageContext函数从画板上将图片扣下来,获取到一个UIImage类型的对象newImage,代码如下所示:
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- CGRect rect = CGRectMake(0, 0, 240, 300);
- //创建画板(上下文)
- UIGraphicsBeginImageContext(rect.size);
- //对画板进行裁剪
- UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
- [path addClip];
- //要画的图片
- UIImage *image = [UIImage imageNamed:@"7.jpg"];
- //在画板上画
- [image drawInRect:rect];
- //从画板中扣出一张画好的图片
- UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
- }
最后创建一个UIImageView对象imageView用于显示绘制完成的图片,即将imageView的image属性设置为newImage,并添加到父视图中,代码如下所示:
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- CGRect rect = CGRectMake(0, 0, 240, 300);
- //创建画板(上下文)
- UIGraphicsBeginImageContext(rect.size);
- //对画板进行裁剪
- UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
- [path addClip];
- //要画的图片
- UIImage *image = [UIImage imageNamed:@"7.jpg"];
- //在画板上画
- [image drawInRect:rect];
- //从画板中扣出一张画好的图片
- UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
- //将画好的图片用UIImageView显示出来
- UIImageView *imageView = [[UIImageView alloc]initWithImage:newImage];
- rect.origin = CGPointMake(40, 80);
- imageView.frame = rect;
- [self.view addSubview:imageView];
- }
7.4 完整代码
本案例中,TRViewController.m文件中的完整代码如下所示:
- #import "TRViewController.h"
- #import "TRImageView.h"
- @interface TRViewController ()
- @property (weak, nonatomic) IBOutlet TRImageView *myImageView;
- @end
- @implementation TRViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- self.myImageView.image = [UIImage imageNamed:@"6.jpg"];
- }
- @end
本案例中,TRImageView.m文件中的完整代码如下所示:
- @interface TRImageView : UIView
- @property (strong, nonatomic) UIImage *image;
- @end
本案例中,TRImageView.m文件中的完整代码如下所示:
- #import "TRImageView.h"
- @implementation TRImageView
- - (void)setImage:(UIImage *)image
- {
- _image = image;
- [self setNeedsDisplay];
- }
- - (void)drawRect:(CGRect)rect
- {
- UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.bounds.size.width / 10];
- //按照路径剪贴绘制上下文
- [path addClip];
- //在指定的位置和空间绘制图像
- [self.image drawInRect:self.bounds];
- }
- @end
本案例中,TRImageViewController.m文件中的完整代码如下所示:
- #import "TRImageViewController.h"
- @implementation TRImageViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- CGRect rect = CGRectMake(0, 0, 240, 300);
- //创建画板(上下文)
- UIGraphicsBeginImageContext(rect.size);
- //对画板进行裁剪
- UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
- [path addClip];
- //要画的图片
- UIImage *image = [UIImage imageNamed:@"7.jpg"];
- //在画板上画
- [image drawInRect:rect];
- //从画板中扣出一张画好的图片
- UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
- //将画好的图片用UIImageView显示出来
- UIImageView *imageView = [[UIImageView alloc]initWithImage:newImage];
- rect.origin = CGPointMake(40, 80);
- imageView.frame = rect;
- [self.view addSubview:imageView];
- }
- @end