UI部分-绘图与滤镜

iOS绘图

概述:

在iOS中可以很容易开发出绚丽的界面效果,是基于两大图形绘制框架-Quartz 2D绘制2D图形和Core Image中强大的滤镜功能.

1.Quartz 2D-二维绘图引擎

简介:

是iOS常用的绘图框架,可以跨平台,经包装的函数库,属于Core Graphics框架一部分.Quartz 2D在UIKIt中也有很好的封装和集成,平时开发所用到得UIKit中组件都是由Core Graphics进行绘制的,且引入UIKit框架时会自动引入Core Graphics框架,并且还对常用的绘图API进行了封装,方便使用;

1.1 图形上下文

CGContextRef类型,表示一个绘制目标(也就是你打算绘制的位置),包含了绘制图形的一些设备信息,Quartz 2D中的所有对象最终都必须绘制到图形上下文。作用: 保存绘图信息, 绘图状态 / 决定绘制输出目标(位图Bitmap、PDF、窗口Window、层Layer、打印对象Printer)

由于每次渲染需要清空上一次的绘图状态, 所以引入图形上下文栈,保存初始状态 save, 之后绘制另一个前获取CGContextRestoreGState 初始绘图状态.

注意: 图形上下文在栈里save几次,只能restore几次.


一般功能:

  • 绘制图形:线/长方形等
  • 绘制文字
  • 绘制图片
  • 生成读取PDF
  • 截屏,裁剪图片
  • 自定义控件-(有时候UI界面普通控件难以实现,此时可以用Quartz 2D将内部结构画出来.) Quartz2D在iOS开发中的主要价值(注意,自定义控件一定要重写新增属性的set方法.)

一般步骤:

  1. 开启或获取图形上下文.
  2. 创建并设置路径. ( 若有矩阵变化,写在此处 )
  3. 添加路径到上下文上. ( 若有设置绘图状态,写在此处 )
  4. 绘制路径 (上下文渲染)
  5. 释放路径 (关闭图形上下文 - 自己创建的 )

1.2基本图形绘制.

UIKit中默认为我们准备好了一个图形上下文,在drawRect:( 这个方法在viewDidLoad之后调用 )中通过UIKit封装函数UIGraphicsGetCurrentContext( ) 获取.

1.2.1简单图形绘制
-(void)drawRect:(CGRect)rect { //绘制线
//1.获得图形上下文
CGContextRef context = UIGraphicsGetCurrentContext();

//2.直接绘制路径到上下文(相当于创建路径并添加路径到图形上下文两步操作)
CGContextMoveToPoint(context, 20, 50);
CGContextAddLineToPoint(context, 20, 100);
CGContextAddLineToPoint(context, 300, 100);
//封闭路径:a.创建一条起点和终点的线,不推荐
//CGPathAddLineToPoint(path, nil, 20, 50);
//封闭路径:b.直接调用路径封闭方法 自动连线
CGContextClosePath(context);

/*2.使用贝塞尔路径绘制路径
UIBezierPath *path = [UIBezierPath bezierPath];    
[path moveToPoint:CGPointMake(0, 125)];
[path addLineToPoint:CGPointMake(125, 250)];
[path addLineToPoint:CGPointMake(250, 125)];
//关闭路径,路径的终点连接起点
[path close]
  3.需要 再添加路径到上下文
CGContextAddPath(ctx, path.CGPath);
*/

//3.设置图形上下文属性
[[UIColor redColor]setStroke];//设置红色边框
[[UIColor greenColor]setFill];//设置绿色填充
//[[UIColor blueColor]set];//同时设置填充和边框色

//4.绘制路径/渲染
// 描边   CGContextStrokePath(context);
// 填充   CGContextFillPath(context);
// 即描边又填充
CGContextDrawPath(context, kCGPathEOFillStroke);
}

如果不需要既描边又填充,可使用UIKit封装的接口:

- (void)drawLine2
{
//贝塞尔路径
UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(50, 50)];

[path addLineToPoint:CGPointMake(100, 100)];

[path stroke];
//    [path fill];
}


- (void)drawLine3
{
// 1. 获取图形上下文
// CGContextRef ctx = UIGraphicsGetCurrentContext();

UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(50, 50)];

[path addLineToPoint:CGPointMake(100, 100)];

// 渲染之前可以给内容设置绘图状态
[[UIColor greenColor]setStroke];
//    CGContextSetLineWidth(ctx, 10);// 线宽
//    CGContextSetLineCap(ctx, kCGLineCapRound); // 圆角    
path.lineWidth = 10;
path.lineCapStyle = kCGLineCapRound;

[path stroke];
//    [path fill];

绘制矩形路径:

//第一种 绘制矩形,相当于创建路径、添加路径到上下文 两步骤
CGContextAddRect( context,rect );   
//第二种:绘制矩形,相当于创建路径、添加路径到上下文、渲染三个步骤
UIRectFill(rect);    //绘制矩形(只有填充)
//UIRectFrame(rect2);//绘制矩形(只有边框)
//第三种:贝塞尔路径绘制矩形 
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 150, 150) cornerRadius:75];
->添加->渲染

绘制椭圆路径:

//第一种
CGContextAddEllipseInRect( context,rect )  
->渲染
//第二种
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 150, 100)];
->添加
->渲染

绘制圆弧路径:

//方法1. 
CGContextAddArc ( x,y,radius半径,startAngle起始弧度,endAngle,closewise是否逆时针绘制,0为顺时针 )
CGContextDrawPath(context, kCGPathFillStroke);

//方法2.贝塞尔路径直接创建  
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(125, 125) radius:100 startAngle:0 endAngle:M_PI_2 clockwise:YES];
[path addLineToPoint:CGPointMake(125, 125)];
[path fill]
1.2.2贝塞尔路径:

主要于曲线绘制,也称贝塞尔曲线.Quartz 2D中曲线绘制分位两种 : 二次贝塞尔曲线和三次贝塞尔曲线.二次曲线只有一个控制点,而三次曲线有两个控制点,如下图所示:

图片名称
//使用贝塞尔路径 绘制曲线.
CGContextMoveToPoint (context,20,100)  //移动到起始位置.
//绘制二次贝塞尔曲线:
/*参数说明: 
参数1:图形上下文
参数2和3:控制点x坐标,y坐标
参数4和5:结束点坐标
*/
CGContextAddQuadCurveToPoint(context, 160, 0, 300, 100);

//绘制三次贝塞尔曲线
 /*参数说明: 
参数1:图形上下文
参数2和3:控制点1的x坐标,y坐标
参数4和5:控制点2的坐标
参数6和7:结束点坐标
*/
CGContextAddCurveToPoint(context, 80, 300, 240, 500, 300, 300);

备注:贝塞尔曲线是由法国数学家“贝塞尔”发现的,他发现:任何一条曲线都能够由和它相切的直线的两个端点来描述,这种曲线表示方式后来被广泛应用到计算机中,称为“贝塞尔曲线”。

1.2.3文本绘制
- (void)drawText
{
// UIKit绘图,画文字
NSString *text = @"sdafsafgsdafsafgsdafsafgsdafsafgsdafsafg";

// 如何去找这富文本属性,导入UIKit框架,在第一个头文件里面,富文本的Key
NSMutableDictionary *attributesDict = [NSMutableDictionary dictionary];

//    [attributesDict setObject:[UIFont systemFontOfSize:20] forKey:NSFontAttributeName]; 
attributesDict[NSFontAttributeName] = [UIFont systemFontOfSize:20];
attributesDict[NSForegroundColorAttributeName] = [UIColor redColor];
attributesDict[NSStrokeWidthAttributeName] = @"5";

// Attributes:富文本属性
[text drawAtPoint:CGPointMake(0, 0) withAttributes:attributesDict];//从某点开始绘制

// [text drawInRect:rect withAttributes:attributesDict];//绘制固定区域,会自动换行
}
/*

可在drawRect:方法之外 设置文本效果:

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 设置阴影
NSShadow *shadow = [[NSShadow alloc]init];
shadow.shadowColor = [UIColor redColor];
shadow.shadowOffset = CGSizeMake(3, 3);
shadow.shadowBlurRadius = 3;

NSMutableDictionary *dictM = [NSMutableDictionary dictionary];

dictM[NSShadowAttributeName] = shadow;

NSAttributedString *strA1 = [[NSAttributedString alloc]initWithString:@"hjkhkjhkjh" attributes:dictM];

// 图文混排

// 附件
NSTextAttachment *ment = [[NSTextAttachment alloc]init];
ment.image = [UIImage imageNamed:@"vip"];

NSAttributedString *strA = [NSAttributedString attributedStringWithAttachment:ment];
NSMutableAttributedString *strAM = [[NSMutableAttributedString alloc]initWithAttributedString:strA];
[strAM appendAttributedString:strA1];

self.label.attributedText = strAM;

}
1.2.4图像绘制
UIImage *image = [UIImage imageNamed:@"xxx"];
//从某点开始
[image drawAtPoint:CGPointMake(10,50)];
//绘制到指定区域, 注意:会根据区域大小进行拉伸
[image drawInRect:CGRectMake(20,100,200,200);
//平铺绘制
[image drawAsPatternInRect:CGRectMake(0, 0, 320, 568)];

1.2.5 矩形变化
-(void)drawRect:(CGRect)rect{
CGContextRef context=UIGraphicsGetCurrentContext();
[self drawImage:context];
}

#pragma mark 图形上下文形变
-(void)drawImage:(CGContextRef)context{
//保存初始状态
CGContextSaveGState(context);

//形变第一步:图形上下文向右平移40
CGContextTranslateCTM(context, 100, 0);

//形变第二步:缩放0.8
CGContextScaleCTM(context, 0.8, 0.8);

//形变第三步:旋转
CGContextRotateCTM(context, M_PI_4/4);

UIImage *image=[UIImage imageNamed:@"photo1.jpg"];
//绘图
[image drawInRect:CGRectMake(0, 50, 240, 300)];


//恢复到初始状态
CGContextRestoreGState(context);

}

注意:

  1. 为什么要实现drawRect:方法,因为只有在drawRect:方法中才能获取到上下文
  2. 矩阵变化:给上下文做矩阵变化,一定要写在上下文添加路径之前. 应为添加之后,上下文已经盖印上layer上了告诉layer哪些区域准备接收, 别的绘图状态等可以写在后面, 但要在渲染之前;

1.3 延伸

1.3.1 渐变填充

有时候纯色的填充并不能满足我们的需求,有时绘制图形需要设置一个漂亮的背景,这是可以选择渐变填充方式.Quartz 2D的渐变方式分为两种:

  1. 线性渐变:渐变色以直线方式从开始位置向结束位置渐变.
  2. 径向渐变:一中心点为圆心从起始渐变色向四周辐射,直到终止渐变色.

原理:先设置从开始位置到结束位置的渐变颜色,只要在 指定位置 设置不同的颜色,剩下的系统会帮我处理.例如:如下图在起始位置、3/10位置、结束位置指定了三种颜色就形成由三种颜色组成的渐变色:


图片名称

还要注意一点,iOS中绘制渐变填充所设置颜色需要指定颜色空间,比如RGB就是一个颜色空间;

-(void)drawRect:(CGRect)rect{
1.线性渐变:
//创建要使用的颜色空间;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

/*指定渐变色
    space:颜色空间
    components:颜色数组 --由于指定了RGB颜色空间,那么要用四个数组元素来表示一个颜色.红.绿.蓝与透明;
    locations:颜色所在位置(范围0-1),这个数组的个数不小于components中存放的颜色个数.
    count:渐变个数.等于locations数;
*/
 CGFloat compoents[12]={
    248.0/255.0,86.0/255.0,86.0/255.0,1,
    249.0/255.0,127.0/255.0,127.0/255.0,1,
    1.0,1.0,1.0,1.0
};   // 3个四组元素,表示指定三个颜色渐变
CGFloat locations[3]={0,0.3,1.0}; //参照物为为所在父控件.
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3);

 /*绘制线性渐变
     context:图形上下文
     gradient:渐变色
     startPoint:起始位置
     endPoint:终止位置
     options:绘制方式,kCGGradientDrawsBeforeStartLocation 开始位置之前就进行绘制,到结束位置之后不再绘制,
         kCGGradientDrawsAfterEndLocation开始位置之前不进行绘制,到结束点之后继续填充
 */
CGContextDrawLinearGradient(context, gradient, CGPointZero, CGPointMake(320, 300), kCGGradientDrawsAfterEndLocation);


2.径向渐变

// 指定渐变色
    同上
  
/*绘制
  context: 上下文
  gradient: 渐变色
  startCenter: 起始位置
  startRadius: 起始半径 (一般为0,否则在此半径内不会有填充)
  endCenter: 结束位置
  endRadius: 终点半径 (需要渐变填充的半径).
  options: 方式,同上
*/
CGContextDrawRadialGradient(context, gradient, CGPointMake(160, 284),0, CGPointMake(165, 289), 150, kCGGradientDrawsAfterEndLocation);

//最后注意释放颜色空间
CGColorSpaceRelease(colorSpace);
}

如果需要仅对绘制的图形进行渐变,则要进行裁剪,且裁剪要在调用渐变之前;

UIRectClip(CGRectMake(20, 50, 280, 300));
//或者 CGContextClipToRect(context, CGRectMake(20, 50, 280, 300));
1.3.2 叠加模式

使用Quartz 2D绘图,后面绘制的图像会覆盖前面的,被覆盖不可见.可以用CGContextSetBlendMode(CGContextRef context, CGBlendMode mode)方法进行设置叠加状态。

1.3.3 自定义填充模式.

除了前面的纯色填充,渐变色填充,还有一种类似贴瓷砖的样式填充.我们只需要绘制一个瓷砖样式,然后让程序自动调用这种样式填充指定大小的区域;

Quartz 2D支持两种填充模式:有颜色填充和无颜色填充。两种模式使用起来区别很小,有颜色填充就是在绘制瓷砖时就指定颜色,而在调用填充时就不用再指定瓷砖颜色; 无颜色填充模式就是绘制瓷砖时不用指定任何颜色,在调用填充时再指定具体填充颜色。相比较无颜色填充模式而言,有颜色填充模式更加的灵活,推荐使用。

有色填充模式步骤:

  1. 构建符合CGPatternDrawPatternCallback签名的方法.这个方法专门用来创建“瓷砖”;再绘制瓷砖样式;
  2. 指定一个填充的颜色空间;
  3. 使用CGPatternCreate方法创建一个填充模式Pattern.
  4. 最后调用CGContextSetFillPattern方法给图形上下文指定填充模式.
  5. 绘制要填充的图形.
  6. 释放颜色空间和填充模式;

代码:

#pragma mark - 有颜色填充模式
void drawColoredTile(void *info,CGContextRef context){
//有颜色填充,这里设置填充色
CGContextSetRGBFillColor(context, 254.0/255.0, 52.0/255.0, 90.0/255.0, 1);
CGContextFillRect(context, CGRectMake(0, 0, TILE_SIZE, TILE_SIZE));
CGContextFillRect(context, CGRectMake(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE));
}

- (void)drawRect:(CGRect)rect {
//获取上下文
    CGContextRef context=UIGraphicsGetCurrentContext();
//模式填充颜色空间,注意对于有颜色填充模式,这里传NULL
CGColorSpaceRef colorSpace=CGColorSpaceCreatePattern(NULL);
//将填充色颜色空间设置为模式填充的颜色空间
CGContextSetFillColorSpace(context, colorSpace);

//填充模式回调函数结构体
CGPatternCallbacks callback={0,&drawColoredTile,NULL};
/*填充模式
 info://传递给callback的参数
 bounds:瓷砖大小
 matrix:形变
 xStep:瓷砖横向间距
 yStep:瓷砖纵向间距
 tiling:贴砖的方法
 isClored:绘制的瓷砖是否已经指定了颜色(对于有颜色瓷砖此处指定位true)
 callbacks:回调函数
 */
CGPatternRef pattern=CGPatternCreate(NULL, CGRectMake(0, 0, 2*TILE_SIZE, 2*TILE_SIZE), CGAffineTransformIdentity,2*TILE_SIZE+ 5,2*TILE_SIZE+ 5, kCGPatternTilingNoDistortion, true, &callback);

CGFloat alpha=1;
//注意最后一个参数对于有颜色瓷砖指定为透明度的参数地址,对于无颜色瓷砖则指定当前颜色空间对应的颜色数组
CGContextSetFillPattern(context, pattern, &alpha);

UIRectFill(CGRectMake(0, 0, 320, 568));

//释放
CGColorSpaceRelease(colorSpace);
CGPatternRelease(pattern);
}

效果:

图片名称

这里强调一点,在drawTile回调方法中不要使用UIKit封装方法进行图形绘制(例如UIRectFill等),由于这个方法由Core Graphics内部调用,而Core Graphics考虑到跨平台问题,内部是不允许调用UIKit方法的。

1.4 重绘.

在UIView的drawRect:中绘制的图形会在控件显示的时候调用一次,但有时候我们希望绘制内容的显示是实时的,此时我们就需要调用绘图方法重新绘制.由于不能在drawRect之外后的图形上下文,所以直接调用setNeedsDisplay方法;

1.5 绘制到位图

位图图形上下文和PDF图形上下文UIKit是不会负责创建的,所以需要用户手动创建,并且在使用完后关闭它。由于这两类图形上下文是由我们手动创建的因此可以放到任何方法中调用。此外,这两个方法开启的图形上下文并没有返回值,如果我们要得到我们创建的图形上下文只要在创建上下文之后、关闭之前调用UIGraphicsGetCurrentContext()方法,此时取得的上下文即是我们自己创建的图形上下文。

1.5.1 例子:添加水印.

首先开启上下文,然后在上下文中绘制图片、直线和文本,最后从当前位图上下文中取得最终形成的新图片显示到界面。

// 加载图片
UIImage *img = [UIImage imageNamed:@"img"];
// 1. 开启位图上下文     参数对应: 尺寸 , opaque , 缩放
UIGraphicsBeginImageContextWithOptions(img.size, NO, 0);
//2.拼接路径 
    //2.1绘制图片
[img drawAtPoint:CGpointZero];
    // 2.2绘制水印文字
 NSString *str = @"我要高薪!!!!!";
[str drawAtPoint:CGPointMake(130, 170) withAttributes:nil];
//3.  从上下文取出图片 (把绘制完成的内容生成一张新图片);
img = UIGraphicsGetImageFromCurrentImageContext();
//4. 关闭上下文
UIGraphicsEndImageContext();

//图片写入,需要转换层二进制数据.对数据进行存储.
NSData *data = UIImagePNGRepresentation(img);
[data writeToFile:@"/Users/apple/Desktop/1.png" atomically:YES];

drawRect:方法中绘制图形效率更高,它不用每次展示时都调用所有图形绘制方法。

1.5.1 例子:圆形裁剪—内切

步骤:

// 开启位图上下文
UIGraphicsBeginImageContextWithOptions(img.size, NO, 0);

// 如要添加圆环,加上下面三行代码
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:bigCricleRect];
    // 设置圆环颜色
[color set];
    //填充和drawAtPoint 
[path fill];

// 设置剪裁区域,默认只会影响后面绘制的内容,会把超出剪裁区域的内容剪裁掉
// 描述圆形路径
UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:circleRect];
// 设置裁剪区域为圆形路径,根据路径设置裁剪区域
[clipPath addClip];

// 画图片
[img drawAtPoint:CGPointZero]; // 若有圆环,那么画图的位置要注意下间距.

// 获取图片
img = UIGraphicsGetImageFromCurrentImageContext();

// 关闭上下文
UIGraphicsEndImageContext();
1.5.2 屏幕截图:
// 开启位图上下文
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);

// 获取当前上下文->位图上文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 拼接路径
// view上面的内容添加到上下文
// view的图层
// 反渲染到上下文中
[self.view.layer renderInContext:ctx];

// 获取图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

// 关闭上下文
UIGraphicsEndImageContext();

// 写入到桌面
// 把图片转换成二进制数据
NSData *data = UIImagePNGRepresentation(image);

[data writeToFile:@"/Users/apple/Desktop/2.png" atomically:YES];
1.5.3 屏幕裁剪:
//1. 添加拖拽手势,监听事件 
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[self.imageView addGestureRecognizer:pan];

监听的事件中:
开始拖动-记录开始点,即裁剪区域的原点
开始移动-获取当前点,结合开始点,描述裁剪尺寸;
手指离开-调用裁剪方法.接受裁剪后图片.
最后移除裁剪掉的视图:

[self.clipView removeFromSuperview];
self.clipView = nil;
1.5.3 图片擦除.

原理:擦除.两张视图,把第一张擦掉显示第二张.

拖入UIPanGestureRecognizer,在监听方法中先获取手指位置. CGPoint curP = [pan locationInView:self.imageView];

 // 开启位图上下文
UIGraphicsBeginImageContextWithOptions(self.imageView.bounds.size, NO, 0);

// 绘制图片
[self.imageView.image drawInRect:self.imageView.bounds];

// 获取当前上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

CGFloat wh = 20;
CGRect clearRect = CGRectMake(curP.x - wh * 0.5, curP.y - wh * 0.5, wh, wh);
// 擦除图片
CGContextClearRect(ctx, clearRect);

// 生成一张新的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

// 关闭上下文
UIGraphicsEndImageContext();       
1.5.4 手势解锁.

设置背景图片四种方式

  1. drawRect方法(加载图片-drawAtPoint即可)
  2. 重写loadView,把控制器View设置为imageView;
  3. 拖imageView,
  4. 设置uivew.layer.context

思路:

  • 可以在awakeFromNib (加载xib/sb会调用一次 ) 或者
    initWithCoder:(NSCoder *) aDecoder ( 解析文件就会调用 ) 中 按九宫格创建解锁按钮.
  • 在 layoutSubviews. 设置子控件位置
  • 使用按钮的选中状态,设置按钮不允许交互,目的把事件交给其父控件View进行Move处理,
  • 在touchMove方法中: 获取当前手指位置 然后遍历按钮位置,判断跟按钮在不在一起.
  • 为了选中按钮有序连线 —引入数组属性,来记录选中按钮(有序的)
  • 画线 在 drawRect: 中 最后记得再touchMove 末尾补一个重绘.
  • 为了线随手指位置移动 ,记录一下 ,再在drawRect中添加一个线到手指移动位置
  • 最后 在touchEnded 中 恢复. self.selectBtns makeObjecsPerformSelect:@select( ) WithObject: @NO; 清除数组.
  • 最后在drawRect开始加个判断 如果数组为 0;那么return;
  • 在aweaknib或layuotSubviews中给按钮绑定tag,在touchEnded进行字符串比较,来判断是否解锁.

1.6 绘制到PDF

绘制到PDF则要启用pdf图形上下文,PDF图形上下文的创建使用方式跟位图图形上下文是类似的,需要注意的一点就是绘制内容到PDF时需要创建分页,每页内容的开始都要调用一次IGraphicsBeginPDFPage();方法。

//1. 开启pdf图形上下文
/**
 path:保存的路径
 bounds:pdf文档大小,如果设置为CGRectZero则使用默认值:612*792
 pageInfo:页面设置,为nil则不设置任何信息
 */
UIGraphicsBeginPDFContextToFile(path,CGRectZero,[NSDictionary dictionaryWithObjectsAndKeys:@"Kenshin Cui",kCGPDFContextAuthor, nil]);

//由于pdf文档是分页的,所以首先要创建一页画布供我们绘制 (每页开始调用一次)
UIGraphicsBeginPDFPage();

//2. 绘制内容
NSString *title=@"Welcome to Apple Support";
[title drawInRect:CGRectMake(26, 20, 300, 50) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18],NSParagraphStyleAttributeName:style}];

//3. 关闭PDF上下文
UIGraphicsEndPDFContext();

扩展知识:

  1. Core Graphics是基于C语言的一套框架,开发时无法像使用Obj-C一样调用;
  1. 在Quartz 2D中凡是使用待业"Creat"或者"copy"关键字创建的对象,在使用完毕后需要调用相应方法释放.(原因是第一条)
  1. Quartz 2D是跨平台的,因此其中的方法不能使用UIKit中的对象(UIkit只有在iOS可用)例如用到的颜色只能用CGColorRef而不能用UIColor,但是UIKit中提供了对应的转换方法;

  2. C语言中枚举一般以"K"开头.所以Quartz 2D参数中很多枚举都是k开头的.

  3. Quartz 2D是Core Graphics的一部分,所以API多数以CG开头;

  4. 在使用Quartz 2D绘图API中所有以“Ref”结尾对象,在声明时都不必声明为指针类型;

  5. 在使用Quartz 2D绘图API时,凡是“UI”开头的相关绘图函数,都是UIKit对Core Graphics的封装(主要为了简化绘图操作);

2. Core Image框架

从iOS5.0开始苹果官方已经提供了Core Image框架来帮助开发者进行滤镜特效制作。
滤镜使用过程中常用的基类对象:

  • CIContext : 图像上下文. 用于管理整个图片的处理过程,不同图形上下文将利用不同的图像处理硬件进行图像处理. (在iOS中可以通过 不同的方式 创建图像上下文,例如可以创建基于CPU的图像上下文,创建基于GPU的图像上下方以及创建OpenGL优化过的图像上下文)。
  • CIFilter : 图像处理滤镜,每种滤镜有不同的参数设置.
  • CIImage : Core Image 中的图像类型,主要用于输入和输出图像;

在iOS7中支持127种滤镜效果,对应的方法和参数设置在iOS文档中可以搜索“core image filter reference”一节的内容可查到,里面也有每种滤镜的详细介绍和图片使用效果。

步骤:

  1. 创建CIContext;
  2. 创建滤镜CIFilter;
  3. 创建过滤的原图片;
  4. 调用CIFilter 的 setValue: forKey: 来为滤镜指定源图片
  5. 设置滤镜参数.
  6. 取得输出的图片.

例:美图秀秀

//1. 初始化CIContext
//创建基于CPU的图像上下文
//    NSNumber *number=[NSNumber numberWithBool:YES];
//    NSDictionary *option=[NSDictionary dictionaryWithObject:number forKey:kCIContextUseSoftwareRenderer];
//  CIContext *context=[CIContext contextWithOptions:option];
CIContext *context=[CIContext contextWithOptions:nil];//使用GPU渲染,推荐,但注意GPU的CIContext无法跨应用访问,例如直接在UIImagePickerController的完成方法中调用上下文处理就会自动降级为CPU渲染,所以推荐先在完成方法中保存图像,然后在主程序中调用
//    EAGLContext *eaglContext=[[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES1];
//    CIContext *context=[CIContext contextWithEAGLContext:eaglContext];//OpenGL优化过的图像上下文

//2. 取得滤镜
CIFilter * colorControlsFilter=[CIFilter filterWithName:@"CIColorControls"];


//3. 初始化CIImage源图像
UIImage *image=[CIImage imageWithCGImage:selectedImage.CGImage];
//4. 设置滤镜的输入图片
[colorControlsFilter setValue:image forKey:@"inputImage"];

//5. 设置滤镜参数
#pragma mark 调整饱和度
[_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputSaturation"];

#pragma mark 调整亮度
[colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputBrightness"];

#pragma mark 调整对比度
[colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputContrast"];

//6 . 显示图片
[self setImage]

#pragma mark 将输出图片设置到UIImageView
-(void)setImage{
CIImage *outputImage= [_colorControlsFilter outputImage];//取得输出图像
CGImageRef temp=[_context createCGImage:outputImage fromRect:[outputImage extent]];
_imageView.image=[UIImage imageWithCGImage:temp];//转化为CGImage显示在界面中

CGImageRelease(temp);//释放CGImage对象
}
@end

在上面的代码中除了使用了基于GPU的图像上下文(推荐方式),也创建了其他图像上下文,尽管已经被注释大家还是需要熟悉。
Core Image允许你一次给图像或视频帧叠加多种效果,同时Core Image还能保证强大的处理效率。
和在使用Core Graphics绘图一样,UIKit中也封装了一些方法直接转换为Core Image中的对象,例如UIImage对象可以直接调用CIImage属性转换为CIImage类型。
参考博客

推荐阅读更多精彩内容

  • --绘图与滤镜全面解析 概述 在iOS中可以很容易的开发出绚丽的界面效果,一方面得益于成功系统的设计,另一方面得益...
    韩七夏阅读 1,036评论 1 6
  • 前言:对《iOS开发系列--打造自己的“美图秀秀”》文章做了整理如果对于CGPaht 或CGContextPath...
    LiYaoPeng阅读 5,351评论 4 31
  • 嘿 你知道什么时候最心痛吗? 就是你一直视如珍宝的 突然有一天听说 被别人玩弄在鼓掌
    韭菜姐阅读 19评论 0 0
  • 你拥有整片森林,你喜欢谁就可以摘下谁的果子,我不行,如果我爱一个人,我的果子就会是又苦又涩。 ​​​
    无理的啊晶阅读 13评论 0 0
  • 我们曾许下过誓言,在蔚蓝的天空下,举着右手,许下了我们的梦想。在追梦的路上,我们一再跌倒,不被理解,不被支持,如今...
    朱程亮阅读 22评论 0 1