简介
iOS上,所有的绘制,无论是否采用OpenGL、Quartz、UIKit、或者 Core Animation—都发生在UIView对象的区域内,即子类化
UIView
,在drawRect
方法中进行绘制。视图定义绘制发生的屏幕区域。如果使用系统提供的视图,绘制工作会自动得到处理。然而,如果定义自己的定制视图,则必须自行提供绘制代码。
Core Graphics 绘图
常用绘图函数
// 1、绘制椭圆
CGContextAddEllipseInRect(CGContextRef context, CGRect rect)
// 2、绘制虚线
CGContextSetLineDash(CGContextRef c, CGFloat phase,
const CGFloat lengths[], size_t count)
// 3、绘制圆弧
CGContextAddArcToPoint(CGContextRef c, CGFloat x1, CGFloat y1,
CGFloat x2, CGFloat y2, CGFloat radius)
CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y,
CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
// 4、绘制曲线
CGContextAddCurveToPoint(CGContextRef c, CGFloat cp1x,
CGFloat cp1y, CGFloat cp2x, CGFloat cp2y, CGFloat x, CGFloat y)
// 5、绘制直线
CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
// 6、绘制曲线
CGContextAddRect(CGContextRef __nullable c, CGRect rect)
填充
1、为图形填充颜色的几个函数:
// 1、填充矩形
CGContextFillRect(context, rectangl);
// 2、填充椭圆或圆
CGContextFillEllipseInRect(context, rectangle);
// 3、填充路径
CGContextFillPath(context);
注意:在填充颜色之前首先要调用
CGContextSetFillColorWithColor()
定制填充颜色。
2、代码示例:
- (void)drawRect:(CGRect)rect {
// 获取上下文(画布)
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置线条宽度
CGContextSetLineWidth(context, 3.0);
// 设置线条颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 创建一个椭圆,如果 width = height 则是一个圆
CGRect rectangle = CGRectMake(100, 100, 200, 200);
CGContextAddEllipseInRect(context, rectangle);
CGContextStrokePath(context);
// 填充
// 1、设置填充颜色
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
// 2、填充椭圆
CGContextFillEllipseInRect(context, rectangle);
}
绘制直线
- (void)drawRect:(CGRect)rect {
// 获取上下文(画布)
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置线条宽度
CGContextSetLineWidth(context, 3.0);
// 设置线条颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 确定第一个点
CGContextMoveToPoint(context, 100, 100);
// 确定第二个点
CGContextAddLineToPoint(context, 200, 300);
// 开始绘图
CGContextStrokePath(context);
}
绘制椭圆
- (void)drawRect:(CGRect)rect {
// 获取上下文(画布)
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置线条宽度
CGContextSetLineWidth(context, 3.0);
// 设置线条颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 创建一个椭圆,如果 width = height 则是一个圆
CGRect rectangle = CGRectMake(100, 100, 200, 200);
CGContextAddEllipseInRect(context, rectangle);
CGContextStrokePath(context);
}
绘制三角形
- (void)drawRect:(CGRect)rect {
// 获取上下文(画布)
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置线条宽度
CGContextSetLineWidth(context, 3.0);
// 设置线条颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 创建三个点
CGPoint point1 = CGPointMake(CGRectGetMidX(self.bounds),
CGRectGetMidY(self.bounds) - 80);
CGPoint point2 = CGPointMake(CGRectGetMidX(self.bounds) - 80,
CGRectGetMidY(self.bounds) + 100);
CGPoint point3 = CGPointMake(CGRectGetMidX(self.bounds) + 80,
CGRectGetMidY(self.bounds) + 100);
// 连接三个点
// 1、设置起点
CGContextMoveToPoint(context, point1.x, point1.y);
// 2、设置连接点
CGContextAddLineToPoint(context, point2.x, point2.y);
CGContextAddLineToPoint(context, point3.x, point3.y);
CGContextAddLineToPoint(context, point1.x, point1.y);
// 绘图
CGContextStrokePath(context);
}
绘制虚线
- (void)drawRect:(CGRect)rect {
// 获取上下文(画布)
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置线条宽度
CGContextSetLineWidth(context, 3.0);
// 设置线条颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// lengths 指明虚线如何交替绘制
// lengths {10, 10}表示先绘制10个点,再跳过10个点,再绘制10个点,如此反复绘制;
// lengths {5, 10, 5}表示先绘制5个点,再跳过10个点,再绘制5个点,再跳过5个点,如此反复绘制;
CGFloat lengths[] = {10, 10};
// 第1个参数:上下文
// 第2个参数:phase值,如果设值为5,则在绘制第一段的时候执行(n - 5)个点,n为虚线交替绘制方式第1个元素值。
// 第3个参数:虚线交替绘制方式
// 第4个参数:虚线交替绘制方式集合元素个数
CGContextSetLineDash(context, 0, lengths, 2);
// 设置虚线起点
CGContextMoveToPoint(context, 100, 100);
// 设置虚线终点
CGContextAddLineToPoint(context, 300, 300);
// 开始绘制
CGContextStrokePath(context);
}
绘制弧线
方法1:
- (void)drawRect:(CGRect)rect {
// 获取上下文(画布)
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置线条宽度
CGContextSetLineWidth(context, 3.0);
// 设置线条颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 设置圆心
CGPoint centerPoint = {200, 300};
// 设置半径
CGFloat radius = 100;
// 设置起始角度
CGFloat startAngle = 0;
// 设置结束角度
CGFloat endAngle = M_PI;
// 设置绘制方向:1为顺时针, 0为逆时针
int clockwise = 1;
// 绘制弧线
// 第1个参数:上下文
// 第2个参数:圆心.x
// 第3个参数:圆心.y
// 第4个参数:半径
// 第5个参数:起始角度
// 第6个参数:结束角度
// 第7个参数:旋转方向
CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, startAngle, endAngle, clockwise);
CGContextStrokePath(context);
}
方法2:
- (void)drawRect:(CGRect)rect {
// 获取上下文(画布)
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置线条宽度
CGContextSetLineWidth(context, 3.0);
// 设置线条颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 通过指定2个切点,还有角度,调用CGContextAddArcToPoint()绘制。
// 圆弧起点
CGPoint firstPoint = {100 ,300};
CGPoint middlePoint = {200 ,200};
CGPoint endPoint = {300 ,300};
CGContextMoveToPoint(context, firstPoint.x, firstPoint.y);
CGContextAddArcToPoint(context, middlePoint.x, middlePoint.y, endPoint.x, endPoint.y, 130);
CGContextStrokePath(context);
}
绘制曲线
- (void)drawRect:(CGRect)rect {
// 获取上下文(画布)
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置线条宽度
CGContextSetLineWidth(context, 3.0);
// 设置线条颜色
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 创建四个点
CGPoint firstPoint = {20, 200};
CGContextMoveToPoint(context, firstPoint.x, firstPoint.y);
// 顶峰点
CGPoint topPoint = {90, 40};
// 低峰点
CGPoint bottomPoint = {230, 360};
// 末尾点
CGPoint endPoint = {300, 200};
// 贝兹曲线是通过一个起始点,然后通过两个控制点,还有一个终止点,调用CGContextAddCurveToPoint()函数绘制。
CGContextAddCurveToPoint(context, topPoint.x, topPoint.y, bottomPoint.x, bottomPoint.y, endPoint.x, endPoint.y);
CGContextStrokePath(context);
}
注意:如果需要绘制多个峰值,可继续绘制曲线,只需将二次绘制曲线起点置为上一次曲线终点即可。
UIBezierPath 绘图
绘制多边形
- (void)drawRect:(CGRect)rect {
// 设置画笔颜色
[[UIColor redColor] setStroke];
UIBezierPath * path = [[UIBezierPath alloc]init];
// 设置起点
[path moveToPoint:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))];
// 添加点
[path addLineToPoint:CGPointMake(100, 100)];
[path addLineToPoint:CGPointMake(120, 80)];
[path addLineToPoint:CGPointMake(150, 100)];
// 封闭路径
[path closePath];
// 设直线条宽度
[path setLineWidth:2];
// 设置拐角处的效果为圆角
[path setLineJoinStyle:kCGLineJoinRound];
// 设置结束处的效果为圆角
[path setLineCapStyle:kCGLineCapRound];
// 开始绘制
[path stroke];
// 填充
// 1、设置填充颜色
[[UIColor brownColor] setFill];
// 2、开始填充
[path fill];
}
绘制弧形
- (void)drawRect:(CGRect)rect {
// 设置填充颜色
[[UIColor redColor] setFill];
UIBezierPath * path = [[UIBezierPath alloc]init];
// 设置圆心
[path moveToPoint:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))];
// 画弧
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
[path addArcWithCenter:center // 圆心
radius:80 // 半径
startAngle:0 // 起始角度
endAngle:M_PI_2 // 结束角度
clockwise:YES]; // 是否顺时针
// 封闭路径
[path closePath];
// 填充
[path fill];
}
注意:以上绘图直接在
drawRect
方法里面执行,如果不在此方法中执行,需要通过CAShapeLayer
类渲染。
拓展
CAShapeLayer实现动画绘制
效果展示
代码示例
- (void)viewDidLoad {
[super viewDidLoad];
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.frame = CGRectMake(0, 0, 150, 150);
// 使用position来确定layer的位置
shapeLayer.position = self.view.center;
// clockwise控制顺时针或逆时针
// 绘制路径,画圆
shapeLayer.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(shapeLayer.bounds), CGRectGetMidY(shapeLayer.bounds)) radius:CGRectGetMidX(shapeLayer.bounds) startAngle:0 endAngle:2 * M_PI clockwise:YES].CGPath;
// 设置图层的路径渲染颜色
shapeLayer.strokeColor = [UIColor blueColor].CGColor;
// 设置图层的填充颜色
shapeLayer.fillColor = [UIColor clearColor].CGColor;
// 设置路径宽度
shapeLayer.lineWidth = 2;
[self.view.layer addSublayer:shapeLayer];
// 使用高级动画中的基本动画,做渲染的关键字为strokeEnd
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
// 设置持续时间
basicAnimation.duration = 3;
// 设置起始帧
basicAnimation.fromValue = @0;
// 设置结束帧
basicAnimation.toValue = @1;
// 设置重复次数
basicAnimation.repeatCount = HUGE_VAL;
// 设置是否反向执行
basicAnimation.autoreverses = NO;
// 用一个字符串标识这个动画,内容是任意的。
[shapeLayer addAnimation:basicAnimation forKey:@"strokeEnd"];
}
触摸绘图
效果展示
代码示例
#import "DrawingView.h"
enum {
undoTag = 100, clearTag
};
@interface DrawingView () {
NSMutableArray *_lines; /**< 存储绘制的线 */
}
@end
@implementation DrawingView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor whiteColor];
// 初始化集合
_lines = [NSMutableArray array];
// 撤销
UIButton *undoButton = [UIButton buttonWithType:UIButtonTypeSystem];
undoButton.bounds = CGRectMake(0, 0, 50, 40);
undoButton.center = CGPointMake(self.bounds.size.width - 50, 55);
[self setButtonAttributeWithButton:undoButton title:@"撤销" tag:undoTag];
[self addSubview:undoButton];
// 清空
UIButton *clearButton = [UIButton buttonWithType:UIButtonTypeSystem];
clearButton.bounds = CGRectMake(0, 0, 50, 40);
clearButton.center = CGPointMake(40, 55);
[self setButtonAttributeWithButton:clearButton title:@"清空" tag:clearTag];
[self addSubview:clearButton];
}
return self;
}
- (void)drawRect:(CGRect)rect {
// 异常处理
if (_lines.count == 0) {
return;
}
// 解决点点击之后再绘制奔溃的问题
[_lines removeObject:@[]];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 3.0);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
// 步骤:取出一条线 -》 取得线上的每一个点 -》绘图
for (int i = 0; i < _lines.count; i++) {
NSMutableArray *points = _lines[i];
for (int j = 0; j < points.count - 1; j++) {
CGPoint currentPoint = [points[j] CGPointValue];
CGPoint nextPoint = [points[j + 1] CGPointValue];
// 连接两点
CGContextMoveToPoint(context, currentPoint.x, currentPoint.y);
CGContextAddLineToPoint(context, nextPoint.x, nextPoint.y);
}
}
// 开始绘制
CGContextStrokePath(context);
}
#pragma mark - Touches methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSMutableArray *newPoints = [NSMutableArray array];
[_lines addObject:newPoints];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
NSValue *value = [NSValue valueWithCGPoint:point];
[_lines.lastObject addObject:value];
// 调用此方法会直接调用drawRect方法
[self setNeedsDisplay];
}
#pragma mark - Private methods
- (void)setButtonAttributeWithButton:(UIButton *)button title:(NSString *)title tag:(NSInteger)tag {
button.tag = tag;
button.layer.borderWidth = 0.5;
button.layer.borderColor = [UIColor blackColor].CGColor;
button.layer.cornerRadius = 6;
[button setTitle:title forState:UIControlStateNormal];
[button addTarget:self action:@selector(respondsToButton:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - responds events
- (void)respondsToButton:(UIButton *)sender {
switch (sender.tag) {
case undoTag: {
[_lines removeLastObject];
[self setNeedsDisplay];
}
break;
case clearTag: {
[_lines removeAllObjects];
[self setNeedsDisplay];
}
default:
break;
}
}
@end
注意:
DrawingView
为UIView
的子类;