- Quartz2D 是一个二维绘图引擎
图形上下文
- 图形上下文(Graphics Context): 是一个
CGContextRef
类型的数据 - 作用:
- 保存绘图信息绘图状态
-
决定绘制的输出目标(绘制到什么地方去?)
image.png
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 *)#>
}
网友评论