iOS開發(fā)之--圖形上下文以及繪圖

前言:
《iOS開發(fā)系列--打造自己的“美圖秀秀”》文章做了整理
如果對于CGPaht 或CGContextPath有不理解,那么請查:《IOS圖形繪制路徑 CGPATH & CGCONTEXT相關(guān)聯(lián)的CGPath & UIBezierPath》

在iOS中常用的繪圖框架就是Quartz 2D,Quartz 2D是Core Graphics框架的一部分帐萎,是一個強(qiáng)大的二維圖像繪制引擎诡渴。Quartz 2D在UIKit中也有很好的封裝和集成,我們?nèi)粘i_發(fā)時所用到的UIKit中的組件都是由Core Graphics進(jìn)行繪制的胡陪。不僅如此叹卷,當(dāng)我們引入UIKit框架時系統(tǒng)會自動引入Core Graphics框架,并且為了方便開發(fā)者使用在UIKit內(nèi)部還對一些常用的繪圖API進(jìn)行了封裝绅作。

在iOS中繪圖一般分為以下幾個步驟:

1.獲取繪圖上下文
2.創(chuàng)建并設(shè)置路徑
3.將路徑添加到上下文
4.設(shè)置上下文狀態(tài)
5.繪制路徑
6.釋放路徑
圖形上下文CGContextRef代表圖形輸出設(shè)備(也就是繪制的位置),包含了繪制圖形的一些設(shè)備信息蛾派,Quartz 2D中的所有對象最終都必須繪制到圖形上下文俄认。這樣一來,我們在繪制圖形時就不必關(guān)心具體的設(shè)備信息洪乍,統(tǒng)一了代碼編寫方式(在Quartz 2D中的繪圖上下文可以是位圖Bitmap眯杏、PDF、窗口Window壳澳、層Layer岂贩、打印對對象Printer)。

基本圖形繪制
在UIKit中默認(rèn)已經(jīng)為我們準(zhǔn)備好了一個圖形上下文對象巷波,在UI控件的drawRect:方法(這個方法在loadView萎津、viewDidLoad方法后執(zhí)行)中我們可以通過UIKit封裝函數(shù)UIGraphicsGetCurrentContext()方法獲得這個圖形上下文(注意在其他UI控件方法中無法取得這個對象),然后我們只要按照繪圖步驟一步步執(zhí)行即可抹镊。下面自定義一個KCView繼承自UIView锉屈,重寫drawRect:方法繪制兩條直線說明上面繪圖的步驟:

KCView.m

//繪圖只能在此方法中調(diào)用,否則無法得到當(dāng)前圖形上下文
-(void)drawRect:(CGRect)rect{
    //1.取得圖形上下文對象
    CGContextRef context = UIGraphicsGetCurrentContext();

    //2.創(chuàng)建路徑對象
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, 20, 50);//移動到指定位置(設(shè)置路徑起點(diǎn))
    CGPathAddLineToPoint(path, nil, 20, 100);//繪制直線(從起始位置開始)
    CGPathAddLineToPoint(path, nil, 300, 100);//繪制另外一條直線(從上一直線終點(diǎn)開始繪制)


    //3.添加路徑到圖形上下文
    CGContextAddPath(context, path);

    //4.設(shè)置圖形上下文狀態(tài)屬性
    CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);//設(shè)置筆觸顏色
    CGContextSetRGBFillColor(context, 0, 1.0, 0, 1);//設(shè)置填充色
    CGContextSetLineWidth(context, 2.0);//設(shè)置線條寬度
    CGContextSetLineCap(context, kCGLineCapRound);//設(shè)置頂點(diǎn)樣式,(20,50)和(300,100)是頂點(diǎn)
    CGContextSetLineJoin(context, kCGLineJoinRound);//設(shè)置連接點(diǎn)樣式垮耳,(20,100)是連接點(diǎn)
    /*設(shè)置線段樣式
    phase:虛線開始的位置    lengths:虛線長度間隔(例如下面的定義說明第一條線段長度8颈渊,然后間隔3重新繪制8點(diǎn)的長度線段遂黍,當(dāng)然這個數(shù)組可以定義更多元素)
    count:虛線數(shù)組元素個數(shù)
    */
    CGFloat lengths[2] = { 18, 9 };
    CGContextSetLineDash(context, 0, lengths, 2);
    /*設(shè)置陰影
    context:圖形上下文
    offset:偏移量
    blur:模糊度
    color:陰影顏色
    */
    CGColorRef color = [UIColor grayColor].CGColor;//顏色轉(zhuǎn)化,由于Quartz 2D跨平臺俊嗽,所以其中不能使用UIKit中的對象雾家,但是UIkit提供了轉(zhuǎn)化方法
    CGContextSetShadowWithColor(context, CGSizeMake(2, 2), 0.8, color);

    //5.繪制圖像到指定圖形上下文
    /*CGPathDrawingMode是填充方式,枚舉類型
    kCGPathFill:只有填充(非零纏繞數(shù)填充),不繪制邊框
    kCGPathEOFill:奇偶規(guī)則填充(多條路徑交叉時绍豁,奇數(shù)交叉填充芯咧,偶交叉不填充)
    kCGPathStroke:只有邊框
    kCGPathFillStroke:既有邊框又有填充
    kCGPathEOFillStroke:奇偶填充并繪制邊框
    */
    CGContextDrawPath(context, kCGPathFillStroke);//最后一個參數(shù)是填充類型

    //6.釋放對象
    CGPathRelease(path);
}

簡化繪圖方式

上面的繪圖方式未免顯得有些麻煩争群,其實Core Graphics 內(nèi)部對創(chuàng)建對象添加到上下文這兩步操作進(jìn)行了封裝宾符,可以一步完成腕唧。另外前面也說過UIKit內(nèi)部其實封裝了一些以“UI”開頭的方法幫助大家進(jìn)行圖形繪制纷捞。就拿前面的例子來說我們改進(jìn)一些繪制方法:

-(void)drawLine2{
    //1.獲得圖形上下文
    CGContextRef context=UIGraphicsGetCurrentContext();
    
    //2.繪制路徑(相當(dāng)于前面創(chuàng)建路徑并添加路徑到圖形上下文兩步操作)
    CGContextMoveToPoint(context, 20, 50);
    CGContextAddLineToPoint(context, 20, 100);
    CGContextAddLineToPoint(context, 300, 100);
    //封閉路徑:a.創(chuàng)建一條起點(diǎn)和終點(diǎn)的線,不推薦
    //CGPathAddLineToPoint(path, nil, 20, 50);
    //封閉路徑:b.直接調(diào)用路徑封閉方法
    CGContextClosePath(context);
    
    //3.設(shè)置圖形上下文屬性
    [[UIColor redColor]setStroke];//設(shè)置紅色邊框
    [[UIColor greenColor]setFill];//設(shè)置綠色填充
    //[[UIColor blueColor]set];//同時設(shè)置填充和邊框色
    
    //4.繪制路徑
    CGContextDrawPath(context, kCGPathFillStroke);
}

上面的操作相比前面的方法應(yīng)該說已經(jīng)簡化了不少婴梧,除了路徑之外其他矩形唤冈、橢圓等都有對應(yīng)的創(chuàng)建方法伦糯。另外上面我們也演示了封閉路徑的方法够庙,大家可以運(yùn)行看一下效果晶衷。

其他圖形繪制
相信大家了解了上面的繪制步驟其他圖形繪制并不麻煩蓝纲,下面以一個例子簡單演示一下其他圖形的繪制,包括文字和圖像的繪制晌纫。
繪制矩形

在下面的方法中還可以看到UIKit對繪圖方法的封裝税迷,使用起來更加簡單。

-(void)drawRectWithContext:(CGContextRef)context{
    //添加矩形對象
    CGRect rect=CGRectMake(20, 50, 280.0, 50.0);
    CGContextAddRect(context,rect);
    //設(shè)置屬性
    [[UIColor blueColor]set];
    //繪制
    CGContextDrawPath(context, kCGPathFillStroke);
}

#pragma mark 繪制矩形(利用UIKit的封裝方法)
-(void)drawRectByUIKitWithContext:(CGContextRef)context{
    CGRect rect= CGRectMake(20, 150, 280.0, 50.0);
    CGRect rect2=CGRectMake(20, 250, 280.0, 50.0);
    //設(shè)置屬性
    [[UIColor yellowColor]set];
    //繪制矩形,相當(dāng)于創(chuàng)建對象锹漱、添加對象到上下文箭养、繪制三個步驟
    UIRectFill(rect);//繪制矩形(只有填充)
    
    [[UIColor redColor]setStroke];
    UIRectFrame(rect2);//繪制矩形(只有邊框)
}

繪制橢圓

-(void)drawEllipse:(CGContextRef)context{
    //添加對象,繪制橢圓(圓形)的過程也是先創(chuàng)建一個矩形
    CGRect rect=CGRectMake(50, 50, 220.0, 200.0);
    CGContextAddEllipseInRect(context, rect);
    //設(shè)置屬性
    [[UIColor purpleColor]set];
    //繪制
    CGContextDrawPath(context, kCGPathFillStroke);
}

弧形繪制

-(void)drawArc:(CGContextRef)context{
    /*添加弧形對象
     x:中心點(diǎn)x坐標(biāo)
     y:中心點(diǎn)y坐標(biāo)
     radius:半徑
     startAngle:起始弧度
     endAngle:終止弧度
     closewise:是否逆時針繪制,0則順時針繪制
    */
    CGContextAddArc(context, 160, 160, 100.0, 0.0, M_PI_2, 1);
    
    //設(shè)置屬性
    [[UIColor yellowColor]set];
    
    //繪制
    CGContextDrawPath(context, kCGPathFillStroke);
}

繪制貝塞爾曲線
要繪制規(guī)則圖形在iOS中相當(dāng)簡單哥牍,但是不規(guī)則圖形怎么繪制呢毕泌?此時就要利用路徑。前面我們繪制了直線嗅辣,它和曲線繪制都屬于路徑繪制撼泛。和直線繪制相比曲線繪制就要復(fù)雜一些,但是路徑作為高級動畫的基礎(chǔ)又是我們必須掌握的澡谭,因此這里我們就一起來熟悉一下曲線繪制愿题。在Quartz 2D中曲線繪制分為兩種:二次貝塞爾曲線和三次貝塞爾曲線。二次曲線只有一個控制點(diǎn)蛙奖,而三次曲線有兩個控制點(diǎn)潘酗,

#pragma mark 繪制貝塞爾曲線
-(void)drawCurve:(CGContextRef)context{
    
    //繪制曲線
    CGContextMoveToPoint(context, 20, 100);//移動到起始位置
    /*繪制二次貝塞爾曲線
     c:圖形上下文
     cpx:控制點(diǎn)x坐標(biāo)
     cpy:控制點(diǎn)y坐標(biāo)
     x:結(jié)束點(diǎn)x坐標(biāo)
     y:結(jié)束點(diǎn)y坐標(biāo)
    */
    CGContextAddQuadCurveToPoint(context, 160, 0, 300, 100);
    
    CGContextMoveToPoint(context, 20, 500);
    /*繪制三次貝塞爾曲線
     c:圖形上下文
     cp1x:第一個控制點(diǎn)x坐標(biāo)
     cp1y:第一個控制點(diǎn)y坐標(biāo)
     cp2x:第二個控制點(diǎn)x坐標(biāo)
     cp2y:第二個控制點(diǎn)y坐標(biāo)
     x:結(jié)束點(diǎn)x坐標(biāo)
     y:結(jié)束點(diǎn)y坐標(biāo)
    */
    CGContextAddCurveToPoint(context, 80, 300, 240, 500, 300, 300);
    
    //設(shè)置圖形上下文屬性
    [[UIColor yellowColor]setFill];
    [[UIColor redColor]setStroke];
    
    //繪制路徑
    CGContextDrawPath(context, kCGPathFillStroke);
}

備注:貝塞爾曲線是由法國數(shù)學(xué)家“貝塞爾”發(fā)現(xiàn)的,他發(fā)現(xiàn):任何一條曲線都能夠由和它相切的直線的兩個端點(diǎn)來描述雁仲,這種曲線表示方式后來被廣泛應(yīng)用到計算機(jī)中崎脉,稱為“貝塞爾曲線”

文字繪制

-(void)drawText:(CGContextRef)context{
    //繪制到指定的區(qū)域內(nèi)容
    NSString *str=@"Star Walk is the most beautiful stargazing app you’ve ever seen on a mobile device. It will become your go-to interactive astro guide to the night sky, following your every movement in real-time and allowing you to explore over 200, 000 celestial bodies with extensive information about stars and constellations that you find.";
    CGRect rect= CGRectMake(20, 50, 280, 300);
    UIFont *font=[UIFont systemFontOfSize:18];//設(shè)置字體
    UIColor *color=[UIColor redColor];//字體顏色
    NSMutableParagraphStyle *style=[[NSMutableParagraphStyle alloc]init];//段落樣式
    NSTextAlignment align=NSTextAlignmentLeft;//對齊方式
    style.alignment=align;
    [str drawInRect:rect withAttributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:color,NSParagraphStyleAttributeName:style}];
}

圖像繪制

-(void)drawImage:(CGContextRef)context{
    UIImage *image=[UIImage imageNamed:@"image2.jpg"];
    //從某一點(diǎn)開始繪制
    [image drawAtPoint:CGPointMake(10, 50)];
    //繪制到指定的矩形中,注意如果大小不合適會會進(jìn)行拉伸
//    [image drawInRect:CGRectMake(10, 50, 300, 450)];
    //平鋪繪制
//    [image drawAsPatternInRect:CGRectMake(0, 0, 320, 568)];
}

繪制漸變填充
從前面的示例中我們可以看到如何設(shè)置填充顏色伯顶,事實上很多時候純色的填充并不能滿足我們的需求囚灼,例如有時候我們要繪制一些圖形可能需要設(shè)置一個漂亮的背景骆膝,這個時候我們可能就會選擇漸變填充方式。Quartz 2D的漸變方式分為兩種:

062332057972566.png

a.線性漸變線:漸變色以直線方式從開始位置逐漸向結(jié)束位置漸變

b.徑向漸變:以中心點(diǎn)為圓心從起始漸變色向四周輻射灶体,直到終止?jié)u變色

要做漸變則必須先設(shè)置從開始位置到結(jié)束位置的漸變顏色阅签,做過photoshop的朋友相信對于漸變色設(shè)置并不陌生,只要在指定位置指定不同的顏色蝎抽,剩下的事情交給系統(tǒng)處理即可政钟,如下圖在起始位置、3/10位置樟结、結(jié)束位置指定了三種顏色就形成由三種顏色組成的漸變色:

另外养交,在iOS中繪制漸變還需要注意一點(diǎn)就是指定顏色空間,所謂顏色空間就是不同顏色在不同的維度上取值最終組成一種顏色的過程瓢宦。就拿RGB來說碎连,如果將紅色、綠色驮履、藍(lán)色看成是x鱼辙、y、z軸坐標(biāo)系玫镐,那么在三個坐標(biāo)上分別取0~255范圍內(nèi)的不同值則可以組成各類顏色倒戏。當(dāng)然,不同顏色空間的“坐標(biāo)系”也是不同的(也就是說顏色表示的方式是不同的)恐似,常用的顏色空間除了RGB還有CMYK(印刷業(yè)常用這種顏色模式)杜跷、Gray。

在使用Quartz 2D繪圖時我們的顏色除了使用常規(guī)的方法(如何前面CGContextSetRGBFillColor(CGContextRef context, CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha)方法)設(shè)置RGB和透明度外矫夷,有時還會遇到顏色參數(shù)是一個數(shù)組情況葱椭。如使用顏色空間填充時用到的CGContextSetFillColor(CGContextRef context, const CGFloat *components)方法,這個時候components數(shù)組中具體是如何存儲顏色就要根據(jù)顏色空間而定口四,如果顏色空間使用RGB則數(shù)組中的元素四個為一組,分別是red(紅)秦陋、green(綠)蔓彩、blue(藍(lán))、alpha(透明度)驳概;如果使用CMYK顏色空間赤嚼,那么數(shù)組中的元素五個為一組,分別是cyan(青)顺又、magenta(洋紅)更卒、yellow(黃)、black(黑)稚照、alpha(透明度)蹂空。

下面的代碼分別演示了兩種漸變方式俯萌,具體漸變繪制函數(shù)參數(shù)代碼中已經(jīng)注釋的很清楚了:

-(void)drawRect:(CGRect)rect{
    CGContextRef context=UIGraphicsGetCurrentContext();
//    [self drawLinearGradient:context];
    [self drawRadialGradient:context];
}

#pragma mark 線性漸變
-(void)drawLinearGradient:(CGContextRef)context{
    //使用rgb顏色空間
    CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
    
    /*指定漸變色
     space:顏色空間
     components:顏色數(shù)組,注意由于指定了RGB顏色空間,那么四個數(shù)組元素表示一個顏色(red上枕、green咐熙、blue、alpha)辨萍,
                如果有三個顏色則這個數(shù)組有4*3個元素
     locations:顏色所在位置(范圍0~1)棋恼,這個數(shù)組的個數(shù)不小于components中存放顏色的個數(shù)
     count:漸變個數(shù),等于locations的個數(shù)
     */
    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
    };
    CGFloat locations[3]={0,0.3,1.0};
    CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3);
    
    /*繪制線性漸變
     context:圖形上下文
     gradient:漸變色
     startPoint:起始位置
     endPoint:終止位置
     options:繪制方式,kCGGradientDrawsBeforeStartLocation 開始位置之前就進(jìn)行繪制锈玉,到結(jié)束位置之后不再繪制爪飘,
             kCGGradientDrawsAfterEndLocation開始位置之前不進(jìn)行繪制,到結(jié)束點(diǎn)之后繼續(xù)填充
     */
    CGContextDrawLinearGradient(context, gradient, CGPointZero, CGPointMake(320, 300), kCGGradientDrawsAfterEndLocation);
    
    //釋放顏色空間
    CGColorSpaceRelease(colorSpace);
}

#pragma mark 徑向漸變
-(void)drawRadialGradient:(CGContextRef)context{
    //使用rgb顏色空間
    CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
    
    /*指定漸變色
     space:顏色空間
     components:顏色數(shù)組,注意由于指定了RGB顏色空間拉背,那么四個數(shù)組元素表示一個顏色(red师崎、green、blue去团、alpha)抡诞,
     如果有三個顏色則這個數(shù)組有4*3個元素
     locations:顏色所在位置(范圍0~1),這個數(shù)組的個數(shù)不小于components中存放顏色的個數(shù)
     count:漸變個數(shù)土陪,等于locations的個數(shù)
     */
    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
    };
    CGFloat locations[3]={0,0.3,1.0};
    CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3);
    
    /*繪制徑向漸變
     context:圖形上下文
     gradient:漸變色
     startCenter:起始點(diǎn)位置
     startRadius:起始半徑(通常為0昼汗,否則在此半徑范圍內(nèi)容無任何填充)
     endCenter:終點(diǎn)位置(通常和起始點(diǎn)相同,否則會有偏移)
     endRadius:終點(diǎn)半徑(也就是漸變的擴(kuò)散長度)
     options:繪制方式,kCGGradientDrawsBeforeStartLocation 開始位置之前就進(jìn)行繪制鬼雀,但是到結(jié)束位置之后不再繪制顷窒,
             kCGGradientDrawsAfterEndLocation開始位置之前不進(jìn)行繪制,但到結(jié)束點(diǎn)之后繼續(xù)填充
     */
    CGContextDrawRadialGradient(context, gradient, CGPointMake(160, 284),0, CGPointMake(165, 289), 150, kCGGradientDrawsAfterEndLocation);
    //釋放顏色空間
    CGColorSpaceRelease(colorSpace);
}
@end
經(jīng)向漸變
線性漸變

擴(kuò)展--漸變填充

上面我們只是繪制漸變到圖形上下文源哩,實際開發(fā)中有時候我們還需要填充對應(yīng)的漸變色鞋吉,例如現(xiàn)在繪制了一個矩形,如何填充成漸變色呢励烦?在此可以利用漸變裁切來完成(當(dāng)然利用層CALayer更加方便但這不在今天的話題討論范圍內(nèi))谓着,特別說明一下區(qū)域裁切并不僅僅適用于漸變填充,對于其他圖形繪制仍然適用坛掠,并且注意裁切只能限于矩形裁切赊锚。

-(void)drawRectWithLinearGradientFill:(CGContextRef)context{
    CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
    
    //裁切處一塊矩形用于顯示,注意必須先裁切再調(diào)用漸變
    //CGContextClipToRect(context, CGRectMake(20, 50, 280, 300));
    //裁切還可以使用UIKit中對應(yīng)的方法
    UIRectClip(CGRectMake(20, 50, 280, 300));
    
    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
    };
    CGFloat locations[3]={0,0.3,1.0};
    CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3);

    CGContextDrawLinearGradient(context, gradient, CGPointMake(20, 50), CGPointMake(300, 300), kCGGradientDrawsAfterEndLocation);
    
    //釋放顏色空間
    CGColorSpaceRelease(colorSpace);
}

其他狀態(tài)設(shè)置

常用的圖形上下文狀態(tài)設(shè)置上面基本都用到了屉栓,我們不再一一解釋舷蒲,這里著重說一下疊加模式和填充模式,初學(xué)者對于這兩個狀態(tài)設(shè)置往往容易產(chǎn)生疑惑友多。

疊加模式

使用Quartz 2D繪圖時后面繪制的圖像會覆蓋前面的牲平,默認(rèn)情況下如果前面的被覆蓋后將看不到后面的內(nèi)容,但是有時候這個結(jié)果并不是我們想要的域滥,因此在Quartz 2D中提供了填充模式供開發(fā)者配置調(diào)整纵柿。由于填充模式類別特別多蜈抓,因此下面以一個例子來說明:

-(void)drawRectByUIKitWithContext2:(CGContextRef)context{
    CGRect rect= CGRectMake(0, 130.0, 320.0, 50.0);
    CGRect rect1= CGRectMake(0, 390.0, 320.0, 50.0);
    
    
    CGRect rect2=CGRectMake(20, 50.0, 10.0, 250.0);
    CGRect rect3=CGRectMake(40.0, 50.0, 10.0, 250.0);
    CGRect rect4=CGRectMake(60.0, 50.0, 10.0, 250.0);
    CGRect rect5=CGRectMake(80.0, 50.0, 10.0, 250.0);
    CGRect rect6=CGRectMake(100.0, 50.0, 10.0, 250.0);
    CGRect rect7=CGRectMake(120.0, 50.0, 10.0, 250.0);
    CGRect rect8=CGRectMake(140.0, 50.0, 10.0, 250.0);
    CGRect rect9=CGRectMake(160.0, 50.0, 10.0, 250.0);
    CGRect rect10=CGRectMake(180.0, 50.0, 10.0, 250.0);
    CGRect rect11=CGRectMake(200.0, 50.0, 10.0, 250.0);
    CGRect rect12=CGRectMake(220.0, 50.0, 10.0, 250.0);
    CGRect rect13=CGRectMake(240.0, 50.0, 10.0, 250.0);
    CGRect rect14=CGRectMake(260.0, 50.0, 10.0, 250.0);
    CGRect rect15=CGRectMake(280.0, 50.0, 10.0, 250.0);
    
    CGRect rect16=CGRectMake(30.0, 310.0, 10.0, 250.0);
    CGRect rect17=CGRectMake(50.0, 310.0, 10.0, 250.0);
    CGRect rect18=CGRectMake(70.0, 310.0, 10.0, 250.0);
    CGRect rect19=CGRectMake(90.0, 310.0, 10.0, 250.0);
    CGRect rect20=CGRectMake(110.0, 310.0, 10.0, 250.0);
    CGRect rect21=CGRectMake(130.0, 310.0, 10.0, 250.0);
    CGRect rect22=CGRectMake(150.0, 310.0, 10.0, 250.0);
    CGRect rect23=CGRectMake(170.0, 310.0, 10.0, 250.0);
    CGRect rect24=CGRectMake(190.0, 310.0, 10.0, 250.0);
    CGRect rect25=CGRectMake(210.0, 310.0, 10.0, 250.0);
    CGRect rect26=CGRectMake(230.0, 310.0, 10.0, 250.0);
    CGRect rect27=CGRectMake(250.0, 310.0, 10.0, 250.0);
    CGRect rect28=CGRectMake(270.0, 310.0, 10.0, 250.0);
    CGRect rect29=CGRectMake(290.0, 310.0, 10.0, 250.0);

    
    [[UIColor yellowColor]set];
    UIRectFill(rect);
    
    [[UIColor greenColor]setFill];
    UIRectFill(rect1);
    
    [[UIColor redColor]setFill];
    UIRectFillUsingBlendMode(rect2, kCGBlendModeClear);
    UIRectFillUsingBlendMode(rect3, kCGBlendModeColor);
    UIRectFillUsingBlendMode(rect4, kCGBlendModeColorBurn);
    UIRectFillUsingBlendMode(rect5, kCGBlendModeColorDodge);
    UIRectFillUsingBlendMode(rect6, kCGBlendModeCopy);
    UIRectFillUsingBlendMode(rect7, kCGBlendModeDarken);
    UIRectFillUsingBlendMode(rect8, kCGBlendModeDestinationAtop);
    UIRectFillUsingBlendMode(rect9, kCGBlendModeDestinationIn);
    UIRectFillUsingBlendMode(rect10, kCGBlendModeDestinationOut);
    UIRectFillUsingBlendMode(rect11, kCGBlendModeDestinationOver);
    UIRectFillUsingBlendMode(rect12, kCGBlendModeDifference);
    UIRectFillUsingBlendMode(rect13, kCGBlendModeExclusion);
    UIRectFillUsingBlendMode(rect14, kCGBlendModeHardLight);
    UIRectFillUsingBlendMode(rect15, kCGBlendModeHue);
    UIRectFillUsingBlendMode(rect16, kCGBlendModeLighten);
    
    UIRectFillUsingBlendMode(rect17, kCGBlendModeLuminosity);
    UIRectFillUsingBlendMode(rect18, kCGBlendModeMultiply);
    UIRectFillUsingBlendMode(rect19, kCGBlendModeNormal);
    UIRectFillUsingBlendMode(rect20, kCGBlendModeOverlay);
    UIRectFillUsingBlendMode(rect21, kCGBlendModePlusDarker);
    UIRectFillUsingBlendMode(rect22, kCGBlendModePlusLighter);
    UIRectFillUsingBlendMode(rect23, kCGBlendModeSaturation);
    UIRectFillUsingBlendMode(rect24, kCGBlendModeScreen);
    UIRectFillUsingBlendMode(rect25, kCGBlendModeSoftLight);
    UIRectFillUsingBlendMode(rect26, kCGBlendModeSourceAtop);
    UIRectFillUsingBlendMode(rect27, kCGBlendModeSourceIn);
    UIRectFillUsingBlendMode(rect28, kCGBlendModeSourceOut);
    UIRectFillUsingBlendMode(rect29, kCGBlendModeXOR);
}

相信大家對比代碼和顯示效果并不難發(fā)現(xiàn)每種疊加的效果。例子中只是使用UIKit的封裝方法進(jìn)行疊加模式設(shè)置藐窄,更一般的方法當(dāng)然是使用CGContextSetBlendMode(CGContextRef context, CGBlendMode mode)方法進(jìn)行設(shè)置资昧。

填充模式

前面的示例中已經(jīng)演示過純色填充、漸變填充荆忍,而有時我們需要按一定的自定義樣式進(jìn)行填充格带,這種方式有點(diǎn)類似于貼瓷磚的方式。我們知道如果家里貼地板或瓷磚時刹枉,通常我們會先選擇一種瓷磚樣式叽唱,根據(jù)房間面積我們購買不同量的瓷磚。但是不管買多少微宝,這些瓷磚的樣式都是一模一樣的棺亭。填充模式就是為了達(dá)到這種效果而產(chǎn)生的:我們只需要繪制一個瓷磚的樣式,然后讓程序自動調(diào)用這種樣式填充指定大小的區(qū)域蟋软。

Quartz 2D支持兩種填充模式:有顏色填充和無顏色填充镶摘。兩種模式使用起來區(qū)別很小,有顏色填充就是在繪制瓷磚時就指定顏色岳守,在調(diào)用填充時就不用再指定瓷磚顏色凄敢;無顏色填充模式就是繪制瓷磚時不用指定任何顏色,在調(diào)用填充時再指定具體填充顏色湿痢。相比較無顏色填充模式而言涝缝,有顏色填充模式更加的靈活,推薦使用譬重。

下面我們具體看一下如何按指定模式進(jìn)行圖形填充:

1.在使用填充模式時首先要構(gòu)建一個符合CGPatternDrawPatternCallback簽名的方法拒逮,這個方法專門用來創(chuàng)建“瓷磚”。注意:如果使用有顏色填充模式臀规,需要設(shè)置填充色滩援。例如我們定義一個方法drawTile繪制以下瓷磚(有顏色填充):


2.接著需要指定一個填充的顏色空間,這個顏色空間跟前面繪制漸變的顏色空間不太一樣塔嬉,前面創(chuàng)建漸變使用的顏色空間是設(shè)備無關(guān)的玩徊,我們需要基于這個顏色空間創(chuàng)建一個顏色空間專門用于填充(注意對于有顏色填充創(chuàng)建填充顏色空間參數(shù)為NULL,不用基于設(shè)備無關(guān)的顏色空間創(chuàng)建)邑遏。

3.然后我們就可以使用CGPatternCreate方法創(chuàng)建一個填充模式,創(chuàng)建填充模式時需要注意其中的參數(shù)恰矩,在代碼中已經(jīng)做了一一解釋(這里注意對于有顏色填充模式isColored設(shè)置為true记盒,否則為false)。

4.最后調(diào)用CGContextSetFillPattern方法給圖形上下文指定填充模式(這個時候注意最后一個參數(shù)外傅,如果是有顏色填充模式最后一個參數(shù)為透明度alpa的地址纪吮,對于無顏色填充模式最后一個參數(shù)是當(dāng)前填充顏色空間的顏色數(shù)組)俩檬。

5.繪制圖形,這里我們繪制一個矩形碾盟。

6.釋放資源棚辽。

下面是具體代碼(包含兩種填充模式代碼,可以一一運(yùn)行)

-(void)drawRect:(CGRect)rect{
    CGContextRef context=UIGraphicsGetCurrentContext();
    [self drawBackgroundWithColoredPattern:context];
//    [self drawBackgroundWithPattern:context];
    
}

#pragma mark - 有顏色填充模式
void drawColoredTile(void *info,CGContextRef context){
    //有顏色填充冰肴,這里設(shè)置填充色
    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)drawBackgroundWithColoredPattern:(CGContextRef)context{
    //設(shè)備無關(guān)的顏色空間
//    CGColorSpaceRef rgbSpace= CGColorSpaceCreateDeviceRGB();
    //模式填充顏色空間,注意對于有顏色填充模式屈藐,這里傳NULL
    CGColorSpaceRef colorSpace=CGColorSpaceCreatePattern(NULL);
    //將填充色顏色空間設(shè)置為模式填充的顏色空間
    CGContextSetFillColorSpace(context, colorSpace);
    
    //填充模式回調(diào)函數(shù)結(jié)構(gòu)體
    CGPatternCallbacks callback={0,&drawColoredTile,NULL};
    /*填充模式
     info://傳遞給callback的參數(shù)
     bounds:瓷磚大小
     matrix:形變
     xStep:瓷磚橫向間距
     yStep:瓷磚縱向間距
     tiling:貼磚的方法
     isClored:繪制的瓷磚是否已經(jīng)指定了顏色(對于有顏色瓷磚此處指定位true)
     callbacks:回調(diào)函數(shù)
     */
    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;
    //注意最后一個參數(shù)對于有顏色瓷磚指定為透明度的參數(shù)地址,對于無顏色瓷磚則指定當(dāng)前顏色空間對應(yīng)的顏色數(shù)組
    CGContextSetFillPattern(context, pattern, &alpha);
    
    UIRectFill(CGRectMake(0, 0, 320, 568));
    
//    CGColorSpaceRelease(rgbSpace);
    CGColorSpaceRelease(colorSpace);
    CGPatternRelease(pattern);
}


#pragma mark - 無顏色填充模式
//填充瓷磚的回調(diào)函數(shù)(必須滿足CGPatternCallbacks簽名)
void drawTile(void *info,CGContextRef context){
    CGContextFillRect(context, CGRectMake(0, 0, TILE_SIZE, TILE_SIZE));
    CGContextFillRect(context, CGRectMake(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE));
}
-(void)drawBackgroundWithPattern:(CGContextRef)context{
    //設(shè)備無關(guān)的顏色空間
    CGColorSpaceRef rgbSpace= CGColorSpaceCreateDeviceRGB();
    //模式填充顏色空間
    CGColorSpaceRef colorSpace=CGColorSpaceCreatePattern(rgbSpace);
    //將填充色顏色空間設(shè)置為模式填充的顏色空間
    CGContextSetFillColorSpace(context, colorSpace);
    
    //填充模式回調(diào)函數(shù)結(jié)構(gòu)體
    CGPatternCallbacks callback={0,&drawTile,NULL};
    /*填充模式
     info://傳遞給callback的參數(shù)
     bounds:瓷磚大小
     matrix:形變
     xStep:瓷磚橫向間距
     yStep:瓷磚縱向間距
     tiling:貼磚的方法(瓷磚擺放的方式)
     isClored:繪制的瓷磚是否已經(jīng)指定了顏色(對于無顏色瓷磚此處指定位false)
     callbacks:回調(diào)函數(shù)
     */
    CGPatternRef pattern=CGPatternCreate(NULL, CGRectMake(0, 0, 2*TILE_SIZE, 2*TILE_SIZE), CGAffineTransformIdentity,2*TILE_SIZE+ 5,2*TILE_SIZE+ 5, kCGPatternTilingNoDistortion, false, &callback);
    
    CGFloat components[]={254.0/255.0,52.0/255.0,90.0/255.0,1.0};
    //注意最后一個參數(shù)對于無顏色填充模式指定為當(dāng)前顏色空間顏色數(shù)據(jù)
    CGContextSetFillPattern(context, pattern, components);
    //    CGContextSetStrokePattern(context, pattern, components);
    UIRectFill(CGRectMake(0, 0, 320, 568));
  
    CGColorSpaceRelease(rgbSpace);
    CGColorSpaceRelease(colorSpace);
    CGPatternRelease(pattern);
}

這里強(qiáng)調(diào)一點(diǎn)熙尉,在drawTile回調(diào)方法中不要使用UIKit封裝方法進(jìn)行圖形繪制(例如UIRectFill等)联逻,由于這個方法由Core Graphics內(nèi)部調(diào)用,而Core Graphics考慮到跨平臺問題检痰,內(nèi)部是不允許調(diào)用UIKit方法的包归。

上下文變換

我們知道在UIKit開發(fā)中UIView有一個transform屬性用于控件的形變,其實在繪圖中我們也經(jīng)常用到圖形形變铅歼,這個時候可以借助圖形上下文的形變方法來完成公壤。在弄清形變之前我們要清楚圖形上下文的坐標(biāo)原點(diǎn),因為無論是位移還是旋轉(zhuǎn)都是相對于坐標(biāo)原點(diǎn)進(jìn)行的椎椰。其實Quartz 2D的坐標(biāo)系同UIKit并不一樣厦幅,它的坐標(biāo)原點(diǎn)在屏幕左下方,但是為了統(tǒng)一編程方式俭识,UIKit對其進(jìn)行了轉(zhuǎn)換慨削,坐標(biāo)原點(diǎn)統(tǒng)一在屏幕左上角。注意在設(shè)置圖形上下文形變之前一定要注意保存上下文的初始狀態(tài)套媚,在使用完之后進(jìn)行恢復(fù)缚态。否則在處理多個圖形形變的時候很容易弄不清楚到底是基于怎樣的坐標(biāo)系進(jìn)行繪圖,容易找不到原點(diǎn)(做過html5 canvas繪圖的朋友對這一點(diǎn)應(yīng)該很熟悉堤瘤,在html5中繪圖也經(jīng)常進(jìn)行狀態(tài)保存和恢復(fù))玫芦。下面通過一個圖片的變換演示一下圖形上下文的形變(其他圖形也是一樣的,就不再演示):

-(void)drawRect:(CGRect)rect{
    CGContextRef context=UIGraphicsGetCurrentContext();
    [self drawImage:context];
}

#pragma mark 圖形上下文形變
-(void)drawImage:(CGContextRef)context{
    //保存初始狀態(tài)
    CGContextSaveGState(context);
    
    //形變第一步:圖形上下文向右平移40
    CGContextTranslateCTM(context, 100, 0);
    
    //形變第二步:縮放0.8
    CGContextScaleCTM(context, 0.8, 0.8);
    
    //形變第三步:旋轉(zhuǎn)
    CGContextRotateCTM(context, M_PI_4/4);
    
    UIImage *image=[UIImage imageNamed:@"photo1.jpg"];
    [image drawInRect:CGRectMake(0, 50, 240, 300)];
    
    
    //恢復(fù)到初始狀態(tài)
    CGContextRestoreGState(context);
}

擴(kuò)展--使用Core Graphics繪制圖像

在前面基本繪圖部分本辐,繪制圖像時使用了UIKit中封裝的方法進(jìn)行了圖像繪制桥帆,我們不妨看一下使用Quartz 2D內(nèi)置方法繪制是什么效果。

-(void)drawImage2:(CGContextRef)context{
    UIImage *image=[UIImage imageNamed:@"image2.jpg"];
    //圖像繪制
    CGRect rect= CGRectMake(10, 50, 300, 450);
    CGContextDrawImage(context, rect, image.CGImage);
}

看起來整個圖像是倒過來的慎皱,原因正是前面說的:在Core Graphics中坐標(biāo)系的y軸正方向是向上的老虫,坐標(biāo)原點(diǎn)在屏幕左下角,y軸方向剛好和UIKit中y軸方向相反茫多。而使用UIKit進(jìn)行繪圖之所以沒有問題是因為UIKit中進(jìn)行了處理祈匙,事實上對于其他圖形即使使用Core Graphics繪制也沒有問題,因為UIKit統(tǒng)一了編程方式。但是使用Core Graphics中內(nèi)置方法繪制圖像是存在這種問題的夺欲,如何解決呢跪帝?

其實圖形上下文只要沿著x軸旋轉(zhuǎn)180度,然后向上平移適當(dāng)?shù)母叨燃纯桑ǖ亲⒁獠灰刂鴝軸旋轉(zhuǎn)些阅,這樣得不到想要的結(jié)果)伞剑。可是通過前面介紹的CGContextRotateCTM方法只能通過沿著z軸旋轉(zhuǎn)市埋,此時不妨使用另外一種方法黎泣,那就是在y軸方向縮放-1,同樣可以達(dá)到想要的效果:

-(void)drawImage2:(CGContextRef)context{
    UIImage *image=[UIImage imageNamed:@"image2.jpg"];
    CGSize size=[UIScreen mainScreen].bounds.size;
    CGContextSaveGState(context);
    CGFloat height=450,y=50;
    //上下文形變
    CGContextScaleCTM(context, 1.0, -1.0);//在y軸縮放-1相當(dāng)于沿著x張旋轉(zhuǎn)180
    CGContextTranslateCTM(context, 0, -(size.height-(size.height-2*y-height)));//向上平移
    //圖像繪制
    CGRect rect= CGRectMake(10, y, 300, height);
    CGContextDrawImage(context, rect, image.CGImage);
    
    CGContextRestoreGState(context);
}

視圖刷新

在UIView的drawRect:中繪制的圖形會在控件顯示的時候調(diào)用(而且顯示時會重繪所有圖形)腰素,有時候我們希望繪制內(nèi)容的顯示是實時的聘裁,此時我們就需要調(diào)用繪圖方法重新繪制,但是在iOS開發(fā)中不允許開發(fā)者直接調(diào)用drawRect:方法弓千,刷新繪制內(nèi)容需要調(diào)用setNeedsDisplay方法衡便。下面以一個調(diào)整字體大小的界面演示一下視圖的刷新。

KCView.h

#import <UIKit/UIKit.h>

@interface KCView : UIView

@property (nonatomic,copy) NSString *title;

@property (nonatomic,assign) CGFloat fontSize;

@end

KCView.m

#import "KCView.h"

@implementation KCView

-(void)drawRect:(CGRect)rect{
    
    NSString *str=_title;
    UIFont *font=[UIFont fontWithName:@"Marker Felt" size:_fontSize];
    UIColor *foreignColor=[UIColor redColor];
    [str drawInRect:CGRectMake(100, 120, 300, 200) withAttributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:foreignColor}];
}

@end

KCMainViewController.m

#import "KCMainViewController.h"
#import "KCView.h"

@interface KCMainViewController ()<UIPickerViewDataSource,UIPickerViewDelegate>{
    KCView *_contentView;
    NSArray *_fontSize;
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initLayout];
    
    [self addPickerView];
}

-(void)initLayout{
    _fontSize=@[@15,@18,@20,@22,@25,@28,@30,@32,@35,@40];
    _contentView=[[KCView alloc]initWithFrame:CGRectMake(0, 0, 320, 300)];
    _contentView.backgroundColor=[UIColor whiteColor];
    _contentView.title=@"Hello world!";
    _contentView.fontSize=[_fontSize[0] intValue];
    [self.view addSubview:_contentView];
}

-(void)addPickerView{
    UIPickerView *picker=[[UIPickerView alloc]initWithFrame:CGRectMake(0, 300, 320, 268)];
    picker.dataSource=self;
    picker.delegate=self;
    
    [self.view addSubview:picker];
}

-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return _fontSize.count;
}

-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
    return [NSString stringWithFormat:@"%@號字體",_fontSize[row] ];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
    _contentView.fontSize=[[_fontSize objectAtIndex:row] intValue];
    
    //刷新視圖
    [_contentView setNeedsDisplay];
}
@end

運(yùn)行效果:

其他圖形上下文

前面我們也說過洋访,Quartz 2D的圖形上下方除了可以繪制到層上還可以繪制到位圖镣陕、PDF等,這里我們就介紹一下如何利用Quartz 2D繪制圖像到位圖及PDF中姻政。

上面的示例中一直都是在drawRect:方法中利用UIGraphicsGetCurrentContext()方法取得上下文呆抑,要得到位圖或者PDF的上下文可以利用UIGraphicsBeginImageContext(CGSize size)和UIGraphicsBeginPDFPageWithInfo(CGRect bounds, NSDictionary *pageInfo)方法。位圖圖形上下文和PDF圖形上下文UIKit是不會負(fù)責(zé)創(chuàng)建的汁展,所以需要用戶手動創(chuàng)建鹊碍,并且在使用完后關(guān)閉它。在使用UIKit中系統(tǒng)創(chuàng)建的圖形上下文的時候食绿,我們只能在drawRect:方法中使用侈咕,由于這兩類圖形上下文是由我們手動創(chuàng)建的因此可以放到任何方法中調(diào)用。此外器紧,這兩個方法開啟的圖形上下文并沒有返回值耀销,如果我們要得到我們創(chuàng)建的圖形上下文只要在創(chuàng)建上下文之后、關(guān)閉之前調(diào)用UIGraphicsGetCurrentContext()方法铲汪,此時取得的上下文即是我們自己創(chuàng)建的圖形上下文熊尉。

繪制到位圖
下面利用位圖圖形上下文給一個圖片添加水印,在下面的程序中我們首先創(chuàng)建上下文掌腰,然后在上下文中繪制圖片狰住、直線和文本,最后從當(dāng)前位圖上下文中取得最終形成的新圖片顯示到界面齿梁。

#import <CoreText/CoreText.h>
#import "KCMainViewController.h"
#import "KCView.h"
#import "KCView2.h"
#import "KCView3.h"

@interface KCMainViewController ()

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UIImage *image=[self drawImageAtImageContext];
    UIImageView *imageView=[[UIImageView alloc]initWithImage:image];
    imageView.center=CGPointMake(160, 284);
    
    [self.view addSubview:imageView];
}

#pragma mark 利用位圖上下文添加水印效果
-(UIImage *)drawImageAtImageContext{
    //獲得一個位圖圖形上下文
    CGSize size=CGSizeMake(300, 188);//畫布大小
    UIGraphicsBeginImageContext(size);
    
    UIImage *image=[UIImage imageNamed:@"photo2.png"];
    [image drawInRect:CGRectMake(0, 0, 300, 188)];//注意繪圖的位置是相對于畫布頂點(diǎn)而言催植,不是屏幕
    
    
    //添加水印
    CGContextRef context=UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(context, 200, 178);
    CGContextAddLineToPoint(context, 270, 178);
    
    [[UIColor redColor]setStroke];
    CGContextSetLineWidth(context, 2);
    
    CGContextDrawPath(context, kCGPathStroke);
    
    NSString *str=@"Kenshin Cui";
    [str drawInRect:CGRectMake(200, 158, 100, 30) withAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"Marker Felt" size:15],NSForegroundColorAttributeName:[UIColor redColor]}];
    
    //返回繪制的新圖形
    UIImage *newImage=UIGraphicsGetImageFromCurrentImageContext();
    
    //最后一定不要忘記關(guān)閉對應(yīng)的上下文
    UIGraphicsEndImageContext();
    
    //保存圖片
//    NSData *data= UIImagePNGRepresentation(newImage);
//    [data writeToFile:@"/Users/kenshincui/Desktop/myPic.png" atomically:YES];
    
    return newImage;
}

@end

注意:上面這種方式繪制的圖像除了可以顯示在界面上還可以調(diào)用對應(yīng)方法進(jìn)行保存(代碼注釋中已經(jīng)包含保存方法);除此之外這種方法相比在drawRect:方法中繪制圖形效率更高,它不用每次展示時都調(diào)用所有圖形繪制方法查邢。

繪制到PDF

繪制到PDF則要啟用pdf圖形上下文,PDF圖形上下文的創(chuàng)建使用方式跟位圖圖形上下文是類似的酵幕,需要注意的一點(diǎn)就是繪制內(nèi)容到PDF時需要創(chuàng)建分頁扰藕,每頁內(nèi)容的開始都要調(diào)用一次IGraphicsBeginPDFPage();方法。下面的示例演示了文本繪制和圖片繪制(其他圖形繪制也是類似的):

#import <CoreText/CoreText.h>
#import "KCMainViewController.h"
#import "KCView.h"
#import "KCView2.h"

@interface KCMainViewController ()

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self drawContentToPdfContext];
}

#pragma mark 利用pdf圖形上下文繪制內(nèi)容到pdf文檔
-(void)drawContentToPdfContext{
    
    //沙盒路徑(也就是我們應(yīng)用程序文件運(yùn)行的路徑)
    NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path=[[paths firstObject] stringByAppendingPathComponent:@"myPDF.pdf"];
    NSLog(@"%@",path);
    //啟用pdf圖形上下文
    /**
     path:保存路徑
     bounds:pdf文檔大小芳撒,如果設(shè)置為CGRectZero則使用默認(rèn)值:612*792
     pageInfo:頁面設(shè)置,為nil則不設(shè)置任何信息
     */
    UIGraphicsBeginPDFContextToFile(path,CGRectZero,[NSDictionary dictionaryWithObjectsAndKeys:@"Kenshin Cui",kCGPDFContextAuthor, nil]);
    
    //由于pdf文檔是分頁的邓深,所以首先要創(chuàng)建一頁畫布供我們繪制
    UIGraphicsBeginPDFPage();
    
    NSString *title=@"Welcome to Apple Support";
    NSMutableParagraphStyle *style=[[NSMutableParagraphStyle alloc]init];
    NSTextAlignment align=NSTextAlignmentCenter;
    style.alignment=align;
    [title drawInRect:CGRectMake(26, 20, 300, 50) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18],NSParagraphStyleAttributeName:style}];
    NSString *content=@"Learn about Apple products, view online manuals, get the latest downloads, and more. Connect with other Apple users, or get service, support, and professional advice from Apple.";
    NSMutableParagraphStyle *style2=[[NSMutableParagraphStyle alloc]init];
    style2.alignment=NSTextAlignmentLeft;
//    style2.firstLineHeadIndent=20;
    [content drawInRect:CGRectMake(26, 56, 300, 255) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15],NSForegroundColorAttributeName:[UIColor grayColor],NSParagraphStyleAttributeName:style2}];
    
    UIImage *image=[UIImage imageNamed:@"applecare_folks_tall.png"];
    [image drawInRect:CGRectMake(316, 20, 290, 305)];
    
    UIImage *image2=[UIImage imageNamed:@"applecare_page1.png"];
    [image2 drawInRect:CGRectMake(6, 320, 600, 281)];
    
    //創(chuàng)建新的一頁繼續(xù)繪制其他內(nèi)容
    UIGraphicsBeginPDFPage();
    UIImage *image3=[UIImage imageNamed:@"applecare_page2.png"];
    [image3 drawInRect:CGRectMake(6, 20, 600, 629)];
    
    //結(jié)束pdf上下文
    UIGraphicsEndPDFContext();
}
@end

生成的pdf文檔:

知識補(bǔ)充

1.Core Graphics是基于C語言的一套框架,開發(fā)時無法像使用Obj-C一樣調(diào)用笔刹;

2.在Quartz 2D中凡是使用帶有“Create”或者“Copy”關(guān)鍵字方法創(chuàng)建的對象芥备,在使用后一定要使用對應(yīng)的方法釋放(由于這個框架基于C語言編寫無法自動釋放內(nèi)存);

3.Quartz 2D是跨平臺的舌菜,因此其中的方法中不能使用UIKit中的對象(UIKit只有iOS可用)萌壳,例如用到的顏色只能用CGColorRef而不能用UIColor,但是UIKit中提供了對應(yīng)的轉(zhuǎn)換方法日月;

4.在C語言中枚舉一般以“k”開頭袱瓮,由于Quartz 2D基于C語言開發(fā),所以它也不例外(參數(shù)中很多枚舉都是k開頭的)爱咬;

5.由于Quartz 2D是Core Graphics的一部分尺借,所以API多數(shù)以CG開頭;

6.在使用Quartz 2D繪圖API中所有以“Ref”結(jié)尾對象精拟,在聲明時都不必聲明為指針類型燎斩;

7.在使用Quartz 2D繪圖API時,凡是“UI”開頭的相關(guān)繪圖函數(shù)蜂绎,都是UIKit對Core Graphics的封裝(主要為了簡化繪圖操作)栅表;

Core Image

利用Quartz 2D我們可以繪制各類圖形、圖像荡碾,功能確實強(qiáng)大谨读。用過photoshop的朋友都知道,使用photoshop可以制作各種濾鏡特效坛吁,那么在iOS中能否實現(xiàn)濾鏡呢劳殖?在iOS5.0之前這些算法基本全部要靠程序員編程實現(xiàn),實現(xiàn)過程相當(dāng)復(fù)雜拨脉。從iOS5.0開始蘋果官方已經(jīng)提供了Core Image框架來幫助開發(fā)者進(jìn)行特效制作哆姻。

先來看一下濾鏡使用過程中常用的基類對象:

CIContext:圖像上下文,用于管理整個圖片處理過程玫膀,不同的圖形上下文將利用不同的圖像處理硬件進(jìn)行圖像處理(在iOS中可以通過不同的方式創(chuàng)建圖像上下文矛缨,例如可以創(chuàng)建基于CPU的圖像上下方、創(chuàng)建基于GPU的圖像上下方以及創(chuàng)建OpenGL優(yōu)化過的圖像上下文)。

CIFilter:圖像處理濾鏡箕昭,每種濾鏡有不同的參數(shù)設(shè)置灵妨。

CIImage:Core Image框架中的圖像類型,主要用于輸入和輸出圖像落竹。

在使用濾鏡之前我們先要弄清平臺主要支持哪些濾鏡泌霍,以及這些濾鏡的方法和參數(shù)如何設(shè)置,此時不妨使用下面的方法進(jìn)行打印查看:

#pragma mark 查看所有內(nèi)置濾鏡
-(void)showAllFilters{
    NSArray *filterNames=[CIFilter filterNamesInCategory:kCICategoryBuiltIn];
    for (NSString *filterName in filterNames) {
        CIFilter *filter=[CIFilter filterWithName:filterName];
        NSLog(@"\rfilter:%@\rattributes:%@",filterName,[filter attributes]);
    }
}

在iOS7中打印會發(fā)現(xiàn)有127中濾鏡述召,如果我們把每種濾鏡都介紹一遍恐怕用幾章內(nèi)容也很難介紹詳細(xì)朱转,事實上也沒有這個必要。這些濾鏡使用方法是類似的积暖,只是參數(shù)設(shè)置有所區(qū)別藤为。在iOS文檔中可以搜索“core image filter reference”一節(jié)的內(nèi)容,里面有每種濾鏡的詳細(xì)介紹和圖片使用效果夺刑。

使用Core Image框架創(chuàng)建濾鏡效果一般分為以下幾步:

1.創(chuàng)建圖像上下文CIContext

2.創(chuàng)建濾鏡CIFilter

3.創(chuàng)建過濾原圖片CIImage

4.調(diào)用CIFilter的setValue: forKey:方法為濾鏡指定源圖片

5.設(shè)置濾鏡參數(shù)【可選】

6.取得輸出圖片顯示或保存

大家都知道在美圖秀秀中有一個“增強(qiáng)”功能缅疟,利用它可以調(diào)整照片的飽和度、亮度遍愿、對比度窿吩,其實在Core Image中也有這樣一款濾鏡,下面就以顏色濾鏡來演示一下Core Image中濾鏡的使用错览。

#import "KCMainViewController.h"
#define CONSTROLPANEL_FONTSIZE 12

@interface KCMainViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate>{
    UIImagePickerController *_imagePickerController;//系統(tǒng)照片選擇控制器
    UIImageView *_imageView;//圖片顯示控件
    CIContext *_context;//Core Image上下文
    CIImage *_image;//我們要編輯的圖像
    CIImage *_outputImage;//處理后的圖像
    CIFilter *_colorControlsFilter;//色彩濾鏡
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initLayout];
}


#pragma mark 初始化布局
-(void)initLayout{
    //初始化圖片選擇器
    _imagePickerController=[[UIImagePickerController alloc]init];
    _imagePickerController.delegate =self;

    //創(chuàng)建圖片顯示控件
    _imageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 64, 320, 502)];
    _imageView.contentMode=UIViewContentModeScaleAspectFit;
    [self.view addSubview:_imageView];
    
    //上方導(dǎo)航按鈕
    self.navigationItem.title=@"Enhance";
    self.navigationItem.leftBarButtonItem=[[UIBarButtonItem alloc]initWithTitle:@"Open" style:UIBarButtonItemStyleDone target:self action:@selector(openPhoto:)];
    self.navigationItem.rightBarButtonItem=[[UIBarButtonItem alloc]initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(savePhoto:)];

    
    //下方控制面板
    UIView *controlView=[[UIView alloc]initWithFrame:CGRectMake(0, 450, 320, 118)];
//    controlView.alpha=0.2;
//    controlView.backgroundColor=[UIColor colorWithRed:46.0/255.0 green:178.0/255.0 blue:235.0/255.0 alpha:1];
    [self.view addSubview:controlView];
    //飽和度(默認(rèn)為1纫雁,大于飽和度增加小于1則降低)
    UILabel *lbSaturation=[[UILabel alloc]initWithFrame:CGRectMake(10, 10, 60, 25)];
    lbSaturation.text=@"Saturation";
    lbSaturation.font=[UIFont systemFontOfSize:CONSTROLPANEL_FONTSIZE];
    [controlView addSubview:lbSaturation];
    UISlider *sldStaturation=[[UISlider alloc]initWithFrame:CGRectMake(80, 10, 230, 30)];//注意UISlider高度雖然無法調(diào)整,很多朋友會說高度設(shè)置位0即可倾哺,事實上在iOS7中設(shè)置為0后是無法拖動的
    [controlView addSubview:sldStaturation];
    sldStaturation.minimumValue=0;
    sldStaturation.maximumValue=2;
    sldStaturation.value=1;
    [sldStaturation addTarget:self action:@selector(changeStaturation:) forControlEvents:UIControlEventValueChanged];
    //亮度(默認(rèn)為0)
    UILabel *lbBrightness=[[UILabel alloc]initWithFrame:CGRectMake(10, 40, 60, 25)];
    lbBrightness.text=@"Brightness";
    lbBrightness.font=[UIFont systemFontOfSize:CONSTROLPANEL_FONTSIZE];
    [controlView addSubview:lbBrightness];
    UISlider *sldBrightness=[[UISlider alloc]initWithFrame:CGRectMake(80, 40, 230, 30)];
    [controlView addSubview:sldBrightness];
    sldBrightness.minimumValue=-1;
    sldBrightness.maximumValue=1;
    sldBrightness.value=0;
    [sldBrightness addTarget:self action:@selector(changeBrightness:) forControlEvents:UIControlEventValueChanged];
    //對比度(默認(rèn)為1)
    UILabel *lbContrast=[[UILabel alloc]initWithFrame:CGRectMake(10, 70, 60, 25)];
    lbContrast.text=@"Contrast";
    lbContrast.font=[UIFont systemFontOfSize:CONSTROLPANEL_FONTSIZE];
    [controlView addSubview:lbContrast];
    UISlider *sldContrast=[[UISlider alloc]initWithFrame:CGRectMake(80, 70, 230, 30)];
    [controlView addSubview:sldContrast];
    sldContrast.minimumValue=0;
    sldContrast.maximumValue=2;
    sldContrast.value=1;
    [sldContrast addTarget:self action:@selector(changeContrast:) forControlEvents:UIControlEventValueChanged];
    
    
    //初始化CIContext
    //創(chuàng)建基于CPU的圖像上下文
    //    NSNumber *number=[NSNumber numberWithBool:YES];
    //    NSDictionary *option=[NSDictionary dictionaryWithObject:number forKey:kCIContextUseSoftwareRenderer];
    //    _context=[CIContext contextWithOptions:option];
    _context=[CIContext contextWithOptions:nil];//使用GPU渲染轧邪,推薦,但注意GPU的CIContext無法跨應(yīng)用訪問,例如直接在UIImagePickerController的完成方法中調(diào)用上下文處理就會自動降級為CPU渲染羞海,所以推薦現(xiàn)在完成方法中保存圖像忌愚,然后在主程序中調(diào)用
    //    EAGLContext *eaglContext=[[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES1];
    //    _context=[CIContext contextWithEAGLContext:eaglContext];//OpenGL優(yōu)化過的圖像上下文
    
    //取得濾鏡
    _colorControlsFilter=[CIFilter filterWithName:@"CIColorControls"];

}
#pragma mark 打開圖片選擇器
-(void)openPhoto:(UIBarButtonItem *)btn{
    //打開圖片選擇器
    [self presentViewController:_imagePickerController animated:YES completion:nil];
}
#pragma mark 保存圖片
-(void)savePhoto:(UIBarButtonItem *)btn{
    //保存照片到相冊
    UIImageWriteToSavedPhotosAlbum(_imageView.image, nil, nil, nil);
    UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Sytem Info" message:@"Save Success!" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
    [alert show];
}

#pragma mark 圖片選擇器選擇圖片代理方法
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
    //關(guān)閉圖片選擇器
    [self dismissViewControllerAnimated:YES completion:nil];
    //取得選擇圖片
    UIImage *selectedImage=[info objectForKey:UIImagePickerControllerOriginalImage];
    _imageView.image=selectedImage;
    //初始化CIImage源圖像
    _image=[CIImage imageWithCGImage:selectedImage.CGImage];
    [_colorControlsFilter setValue:_image forKey:@"inputImage"];//設(shè)置濾鏡的輸入圖片
}

#pragma mark 將輸出圖片設(shè)置到UIImageView
-(void)setImage{
    CIImage *outputImage= [_colorControlsFilter outputImage];//取得輸出圖像
    CGImageRef temp=[_context createCGImage:outputImage fromRect:[outputImage extent]];
    _imageView.image=[UIImage imageWithCGImage:temp];//轉(zhuǎn)化為CGImage顯示在界面中
    
    CGImageRelease(temp);//釋放CGImage對象
}

#pragma mark 調(diào)整飽和度
-(void)changeStaturation:(UISlider *)slider{
    [_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputSaturation"];//設(shè)置濾鏡參數(shù)
    [self setImage];
}

#pragma mark 調(diào)整亮度
-(void)changeBrightness:(UISlider *)slider{
    [_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputBrightness"];
    [self setImage];
}

#pragma mark 調(diào)整對比度
-(void)changeContrast:(UISlider *)slider{
    [_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputContrast"];
    [self setImage];
}
@end

再次給大家強(qiáng)調(diào)一下:

在上面的代碼中除了使用了基于GPU的圖像上下文(推薦方式),也創(chuàng)建了其他圖像上下文却邓,盡管已經(jīng)被注釋大家還是需要熟悉硕糊。
Core Image允許你一次給圖像或視頻幀疊加多種效果,同時Core Image還能保證強(qiáng)大的處理效率腊徙。
和在使用Core Graphics繪圖一樣简十,UIKit中也封裝了一些方法直接轉(zhuǎn)換為Core Image中的對象,例如UIImage對象可以直接調(diào)用CIImage屬性轉(zhuǎn)換為CIImage類型撬腾。
原文地址:http://www.cnblogs.com/kenshincui/p/3959951.html#!comments

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末螟蝙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子民傻,更是在濱河造成了極大的恐慌胰默,老刑警劉巖场斑,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異牵署,居然都是意外死亡漏隐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門奴迅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锁保,“玉大人,你說我怎么就攤上這事半沽。” “怎么了吴菠?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵者填,是天一觀的道長。 經(jīng)常有香客問我做葵,道長占哟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任酿矢,我火速辦了婚禮榨乎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘫筐。我一直安慰自己蜜暑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布策肝。 她就那樣靜靜地躺著肛捍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪之众。 梳的紋絲不亂的頭發(fā)上拙毫,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音棺禾,去河邊找鬼缀蹄。 笑死,一個胖子當(dāng)著我的面吹牛膘婶,可吹牛的內(nèi)容都是我干的缺前。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼悬襟,長吁一口氣:“原來是場噩夢啊……” “哼诡延!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起古胆,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤肆良,失蹤者是張志新(化名)和其女友劉穎筛璧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惹恃,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡夭谤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了巫糙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朗儒。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖参淹,靈堂內(nèi)的尸體忽然破棺而出醉锄,到底是詐尸還是另有隱情,我是刑警寧澤浙值,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布恳不,位于F島的核電站,受9級特大地震影響开呐,放射性物質(zhì)發(fā)生泄漏烟勋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一筐付、第九天 我趴在偏房一處隱蔽的房頂上張望卵惦。 院中可真熱鬧,春花似錦瓦戚、人聲如沸沮尿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛹找。三九已至,卻和暖如春哨坪,著一層夾襖步出監(jiān)牢的瞬間庸疾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工当编, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留届慈,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓忿偷,卻偏偏與公主長得像金顿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鲤桥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容