- Quartz2D 是一个二维绘图引擎
图形上下文
- 图形上下文(Graphics Context): 是一个
CGContextRef
类型的数据 - 作用:
- 保存绘图信息绘图状态
-
决定绘制的输出目标(绘制到什么地方去?)
Quartz2D 绘图的代码步骤
1.获得图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
2.拼接路径
CGContextMoveToPoint(ctx,10,10);
CGContextAddLineToPoint(ctx,100,100);
- 3.绘制路径
CGContextStrokePath(ctx)
常用的拼接路径函数
新建一个起点
void CGContextMoveToPoint(CGContextRef c,CGFloat x, CGFloat y)
添加新的线段到某个点
void CGContextAddLineToPoint(CGContextRef c, CGFloat x,CGFloat y)
添加一个矩形
void CGContextAddRect(CGContextRef c,CGRect rect)
添加一个椭圆
void CGContextAddEllipseInRect(CGContextRef context,CGRext rect)
添加一个圆弧
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
Mode参数决定绘制的模式
void CGContextDrawPath(CGContextRef c,CGPathDrawinMode mode)
绘制空心路径
void CGContextStrokePath(CGContextRef c)
绘制实心路径
void CGContextFillPah(CGContextRef c)
图形上下文栈的操作
- 将当前的上下文copy一份,保存到栈顶
void CGContextSaveGState(CGContextRef c)
- 将栈顶的上下文出栈,替换掉当前的上下文
void CGContextRestoreGState(CGContextRef c)
矩阵操作
-
利用矩阵操作,能让绘制到上下文中的所有路径一起发生变化
- 缩放
void CGContextScaleCTM(CGContextRef c,CGFloat sx ,CGFloat sy)
- 旋转
void CGContextRotateCTM(CGContextRef c,CGFloat angle)
- 平移
void CGContextTranslateCTM(CGContextRef c,CGFloat tx ,CGFloat ty)
- 缩放
-
自定义view的基本步骤
- 获取图形上下文
- 创建(描绘)路径
- 把路径添加到上下文
- 渲染上下文
- (Void) drawRect:(CGRect)rect {
// 1.获取图形上下文
// 目前我们所用的上下文都是以UIGraphics
// CGContextRef Ref:引用 CG:目前使用到的类型和函数 一般都是CG开头 CoreGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.描述路径
// 创建路径
CGMutablePathRef path = CGPathCreateMutable();
// 设置起点
// path:给哪个路径设置起点
CGPathMoveToPoint(path, NULL, 50, 50);
// 添加一根线到某个点
CGPathAddLineToPoint(path, NULL, 200, 200);
// 3.把路径添加到上下文
CGContextAddPath(ctx, path);
// 4.渲染上下文
CGContextStrokePath(ctx);
}
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPath];
// 设置起点
[path moveToPoint:CGPointMake(50, 50)];
// 添加一根线到某个点
[path addLineToPoint:CGPointMake(200, 200)];
// 绘制路径
[path stroke];
}
- 为什么要实现drawRect:方法才能绘图到view上?
- 因为在drawRect:方法中才能获取到跟view相关联的图形上下文
- drawRect:方法什么时候调用?
- 当view第一次显示到屏幕上的时(被加到UIWindow上显示出来)
- 调用view的setNeedsDisplay或者setNeedsDisplayInrect时
绘制图片和文字
-
drawInRect:withAttributes
和drawAtPoint:withAttributes
的区别 -
drawInRect:withAttributes
可以换行
NSString *str = @"An empty implementation adversely affects performance during animation.";
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSForegroundColorAttributeName] = [UIColor redColor];
dict[NSFontAttributeName] = [UIFont systemFontOfSize:19];
// 不会换行
[str drawAtPoint:CGPointZero withAttributes:nil];
// 可以换行
[str drawInRect:rect withAttributes:dict];
-
drawAtPoint:
默认绘制的内容和图片的尺寸大小一样 drawInRect
-
drawAsPatternInRect
绘制图片 平铺
定时器
- NSTimer很少用于绘图,因为调度优先级比较低,并不会准时调用,通常使用
CADisplayLink
-
setNeedsDisplay
这个方法并不会马上调用drawRect,其实这个方法只是给当前控件添加刷新的标记,等下一次屏幕刷新的时候才会调用drawRect
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeChange)];
// 添加主运行循环
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
矩阵方法
- (void)drawRect:(CGRect)rect {
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.描述路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-100, -50, 200, 100)];
[[UIColor redColor] set];
// 上下文矩阵操作
// 注意:矩阵操作必须要在添加路径之前
// 平移
CGContextTranslateCTM(ctx, 100, 50);
// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);
// 旋转
CGContextRotateCTM(ctx, M_PI_4);
// 3.把路径添加上下文
CGContextAddPath(ctx, path.CGPath);
[[UIColor redColor] set];
// 4.渲染上下文
CGContextFillPath(ctx);
}
图片水印
- 开启一个基于位图的图形上下文
void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque ,CGFloat scale)
- size: 大小
- opaque: 不透明度 (NO为透明,一般弄透明的上下文 )
- scale: 缩放,通常不需要缩放上下文,取值为0 ,表示不缩放
- 从上下文中获取图片
UIImage * UIGraphicsGetImageFromCurrentImageContext()
- 结束基于位图的图形上下文
void UIGraphicsEndImageContext()
- (Void)waterImage {
// 加载图片
UIImage *image = [UIImage imageNamed:@"nihao"];
// 0.获取上下文,之前的上下文都是在view的drawRect方法中获取(跟View相关联的上下文layer上下文)
// 目前我们需要绘制图片到新的图片上,因此需要用到位图上下文
// 怎么获取位图上下文,注意位图上下文的获取方式跟layer上下文不一样。位图上下文需要我们手动创建。
// 开启一个位图上下文,注意位图上下文跟view无关联,所以不需要在drawRect.
// size:位图上下文的尺寸(新图片的尺寸)
// opaque: 不透明度 YES:不透明 NO:透明,通常我们一般都弄透明的上下文
// scale:通常不需要缩放上下文,取值为0,表示不缩放
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 1.绘制原生的图片
[image drawAtPoint:CGPointZero];
// 2.给原生的图片添加文字
NSString *str = @"我是水印";
[str drawAtPoint:CGPointMake(200, 528) withAttributes:nil];
// 3.生成一张图片给我们,从上下文中获取图片
UIImage *imageWater = UIGraphicsGetImageFromCurrentImageContext();
// 4.关闭上下文
UIGraphicsEndImageContext();
_imageView.image = imageWater;
}
图片剪裁
- 将当前上下文所绘制的路径都剪裁出来(超出这个剪裁区域的都不能显示)
void CGContextClip(CGContextRef c)
- 步骤
- 开启位图上下文
- 设置原型裁剪区域
- 绘制图片
- 从上下文中获取图片
- 关闭上下文
- (void)clipImage
{
// 0.加载图片
UIImage *image = [UIImage imageNamed:@"阿狸头像"];
// 1.开启位图上下文,跟图片尺寸一样大
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 2.设置圆形裁剪区域,正切与图片
// 2.1创建圆形的路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
// 2.2把路径设置为裁剪区域
[path addClip];
// 3.绘制图片
[image drawAtPoint:CGPointZero];
// 4.从上下文中获取图片
UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
// 5.关闭上下文
UIGraphicsEndImageContext();
_imageView.image = clipImage;
}
屏幕截图
- 图片转化成NSData
UIImageJPEGRepresentation
- 将图片保存到指定文件
[data writeToFile:@"/Users/xxx//Desktop/close.png" atomically:YES]
+ (UIImage *)imageWithCaputureView:(UIView *)view
{
// 开启位图上下文
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0);
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 把控件上的图层渲染到上下文,layer只能渲染
[view.layer renderInContext:ctx];
// 生成一张图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
return image;
}
图片截屏
-
pan
手势拖拽
- (void)setuiPan {
// 给控制器的view添加一个pan手势 拖拽
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];
}
- (void)pan:(UIPanGestureRecognizer *)pan {
// 获取偏移量
// 返回的是相对于最原始的手指的偏移量
CGPoint transP = [pan translationInView:self.imageV];
// 移动图控件
self.imageV.transform = CGAffineTransformTranslate(self.imageV.transform, transP.x, transP.y);
// 复位
[pan setTranslation:CGPointZero inView:self.view];
}
- 屏幕截图
- 在一个背景image上移动半透明的view,形成截屏的view
- (void)setupPan {
UIPanGestureRecongnizer *pan = [UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
}
- (void)pan:(UIPanGestureRecognizer *)pan
{
CGPoint endA = CGPointZero;
if (pan.state == UIGestureRecognizerStateBegan) { // 一开始拖动的时候
// 获取一开始触摸点
_startP = [pan locationInView:self.view];
}else if(pan.state == UIGestureRecognizerStateChanged){ // 一直拖动
// 获取结束点
endA = [pan locationInView:self.view];
CGFloat w = endA.x - _startP.x;
CGFloat h = endA.y - _startP.y;
// 获取截取范围
CGRect clipRect = CGRectMake(_startP.x, _startP.y, w, h);
// 生成截屏的view
self.clipView.frame = clipRect;
}else if (pan.state == UIGestureRecognizerStateEnded){
// 图片裁剪,生成一张新的图片
// 开启上下文
// 如果不透明,默认超出裁剪区域会变成黑色,通常都是透明
UIGraphicsBeginImageContextWithOptions(_imageV.bounds.size, NO, 0);
// 设置裁剪区域
UIBezierPath *path = [UIBezierPath bezierPathWithRect:_clipView.frame];
[path addClip];
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 把控件上的内容渲染到上下文
[_imageV.layer renderInContext:ctx];
// 生成一张新的图片
_imageV.image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
// 先移除
[_clipView removeFromSuperview];
// 截取的view设置为nil
_clipView = nil;
}
// 获取手指的偏移量
// pan translationInView:<#(UIView *)#>
}