今天博主有一個Quartz 2D的需求,遇到了一些困難點,在此和大家分享,希望能夠共同進步.
在開始正文之前,首先讓我們解決一個新手經常會問的問題,Quartz 2D和Core Graphics有什么區別和聯系
在iOS中,Quartz 2D是Core Graphics框架的一部分,是Core Graphics的底層引擎,簡單來說,可以將Quartz 2D和Core Graphics劃等號,也就是Quartz 2D==Core Graphics.
引言:Quartz 2D繪圖的核心API是CGContextRef,該API專門用于繪制各種圖形。Quartz 2D是一個二維圖形繪制引擎,它支持iOS環境和Mac OS X環境,為開發者會提供了很多的方便,它在繪圖上功能是非常強大的,如 基于路徑的繪圖、透明度、陰影、顏色管理、反鋸齒、PDF文檔生成和PDF元數據訪問 等。Quartz 2DAPI作為Core Graphics框架的一部分,因此其中的很多數據類型和方法都是以CG開頭的。會經常見到Quartz 2D(Quartz)和Core Graphics兩個術語交互使用。
學習Quartz 2D之前首先先來掌握幾個基礎知識。
1.圖形上下文(Graphics Context)——繪制目標
1)Graphics Context是一個數據類型(CGContextRef),封裝了Quartz繪制圖像到輸出設備的信息。 輸出設備可以是PDF文件、Bitmap、Layer、打印機或者顯示器的窗口上
2)Quartz中所有的 對象都是繪制到一個Graphics Context中
3)當用Quartz繪圖時,所有設備相關的特性都包含在Graphics Context中。換句話說,我們可以簡單地給Quartz繪圖序列指定不同的Graphics Context,就可將相同的圖像繪制到不同的設備上。而不需要任何設備相關的計算,這些都由Quartz替我們完成
2.Quartz 2D坐標系
1) Quartz中默認的坐標系統是:原點(0, 0)在左下角。 沿著X軸從左到右坐標值逐漸增大;沿著Y軸從下到上坐標值逐漸增大
2)有一些技術在設置它們的graphics context時使用了不同于Quartz的默認坐標系統。最常見的一種修改的坐標系統是原點位于左上角,而沿著Y軸從上到下坐標值逐漸增大。例如: UIView中的UIGraphicsGetCurrentContext方法返回的圖形上下文就是用的是這種坐標系(坐標系統的原點位于左上角)
3.UIKit的坐標系
1) 原點(0,0)在屏幕的左上角,X軸向右正向延伸,Y軸向下正向延伸
2)iOS的像素分辨率會隨設備的硬件而變化,iphone4第一次引入了視網膜屏幕,像素分辨率為960* 640,剛好是前一代iPod和iPhone像素分辨率( 480* 320)的兩倍
3)在繪圖時,需要使用“點”的概念來思考問題,而不是像素。也就是說在點坐標系中繪圖,不是硬件的像素坐標系
4)雖然這些設備的像素分辨率不同,但用到的坐標系保持不變(以點為單位)。在iPhone4上,一個點會用2像素寬度來繪制
提示:如果繪圖的上下文,是使用UIGraphicsGetCurrentContext或者其他以UI開頭的方法獲取到的,在繪圖時無需進行坐標轉換
4.Quartz 2D的繪圖順序
后面繪制的圖形,會覆蓋先前繪制的圖形。如下圖所示:
使用Quartz 2D繪圖的關鍵步驟有兩步:
1.獲取CGContextRef;
2.調用CGContextRef的方法進行繪圖。
不同場景下獲取CGContextRef的方式各不相同,下面介紹iOS開發中最常見的場景下如何獲取CGContextRef.
(1)自定義UIView獲取CGContextRef
開發自定義UIView的方法是,開發一個集成UIView的子類,并重寫UIView的drawRect:方法,當該UIVie每次顯示出來時,或該UIView的內容需要更新時,系統都會自動調用UIView的drawRect:方法。在調用UIView的drawRect:方法之前系統會自動配置繪圖環境,因此程序只要通過如下函數即可獲取CGContextRef繪圖API:
CGContextRef context = UIGraphicsGetCurrentContext();
(2)創建位圖時獲取CGContextRef
如果需要在創建位圖時獲取CGContextRef,那么程序需要先調用UIGraphicsBeginImageContext()函數來創建內存中的圖片。然后調用UIGraphicsGetCurrentContext()獲取繪圖的CGContextRef。
Quartz 2D時面向過程的API,Quartz 2D提供了大量函數來完成繪圖。
現在通過一個案例來熟悉一下Quartz 2D的使用過程,以及常用的函數使用。首先為UIView創建一個DemoView類,本案例中所有的繪圖操作都是在DemoView類中進行實現的。
#import "DemoView.h"@implementation DemoView- (void)drawRect:(CGRect)rect { // 獲取繪圖的上下文 CGContextRef context = UIGraphicsGetCurrentContext(); //1.繪制三角形 [self drawTriangle:context]; //2.繪制矩形 [self drawRectangle:context];}//繪制三角形-(void)drawTriangle:(CGContextRef)context{ //2.添加繪圖路徑 CGContextMoveToPoint(context, 100, 100); CGContextAddLineToPoint(context, 200, 100); CGContextAddLineToPoint(context, 150, 200); CGContextAddLineToPoint(context, 100, 100); //3.設置繪圖的屬性 CGFloat myColor[4] = {1.0,0.0,0.0,1.0}; //設置描邊的顏色 CGContextSetStrokeColor(context, myColor); CGFloat myColor1[4]= {0.0,1.0,0.0,1.0}; //設置填充的顏色 CGContextSetFillColor(context, myColor1); //設置線寬 CGContextSetLineWidth(context, 5.0); //設置線的類型:虛線————注意:如果對上下文進行了修改之后的所有連線的類型都默認為虛線啦!其他屬性也一并如此。 CGFloat dash[2] = {1.0,2.0}; CGContextSetLineDash(context, 0, dash, 2); //設置連接點的類型 /* enum CGLineJoin { kCGLineJoinMiter, 連接處為尖角形狀 kCGLineJoinRound, 連接處為圓角形狀 kCGLineJoinBevel 連接處為平角形狀 }; */ CGContextSetLineJoin(context, kCGLineJoinRound); //4.繪圖 CGContextDrawPath(context, kCGPathFillStroke);}//繪制矩形-(void)drawRectangle:(CGContextRef)context{ //添加一個矩形 CGContextAddRect(context, CGRectMake(50, 50, 50, 50)); //添加一個圓形 CGContextAddEllipseInRect(context, CGRectMake(10, 100, 50, 50)); //添加一個橢圓 CGContextAddEllipseInRect(context, CGRectMake(200, 100, 100, 50)); //設置繪圖屬性 CGFloat myColor[4] = {1.0,0.0,0.0,1.0}; //設置描邊顏色 CGContextSetStrokeColor(context, myColor); CGFloat myColor1[4] = {0.0,1.0,0.0,1.0}; //設置填充顏色 CGContextSetFillColor(context, myColor1); //繪圖 CGContextDrawPath(context, kCGPathFillStroke);}@end
運行結果如下圖:
Quartz 2D繪制的線條默認時實線的。上圖中使用的卻是點線,關于點線模式的設置我做多下詳細的介紹:
如果需要創建點線可調用CGContextRef的CGContextSetLineDash(CGContextRef c, CGFloat phase, const CGFloat *lengths, size_t count);該函數的第3個參數是點線模式的關鍵,該參數是一個CGFloat型數組(第4個參數通用用于指定該數組的長度),每個CGFloat值依次控制點線的實現長度、間距。比如該參數如下:
1)- {2,3}:代表長為2的實線、距離為3的間距、長為2的實線、距離為3的間距......這種點線模式。
2)- {2,3,1}:代表長為2的實線、距離為3的間距、長為1的實線、距離為2的間距、長度為3的實線、距離為1的間距......這種點線模式。
3)- {5,3,1,2}:代表長為5的實線、距離為3的間距、長為1的實線、距離為2的間距、長為5的實線、距離為3的間距、長為1的實線、距離為2的間距.....這種點線模式。
該方法的第2個參數用于指定點線的相位,該參數將會與第3個參數協同起作用,比如如下參數組合。
- phase=1,lengths={2,3}:代表長為2的實線、距離為3的間距、長為2的實線、距離為3的間距。但開始繪制起點時只繪制長度為1的實線,因為phase為1就是控制該點線“移動”1個點。
- phase=3,lengths={5,3,1,2}:代表長為5的實線、距離為3的間距、長為1的實線、距離為2的間距、長為5的實線、距離為3的間距、長為1的實線、距離為2的間距。但開始繪制起點時只繪制長度為2的實線,因為phase為3就是控制該點線“移動”3個點。
三、CGContextRef實現文字、圖片、基于路徑的圖形繪制
前面說到CGContextRef不但能提供繪制基本圖形的功能,還可以提供繪制文字、圖片、基于路徑的圖形的繪制。下面來看下對這三種的繪制時Quartz 2D是如何實現的。
1.使用路徑
使用路徑的步驟如下:
1.調用CGContextBeginPath函數開始定義路徑
2.調用各種子函數添加路徑
3.如果路徑添加完成,調用CGContextClosePath函數關閉路徑。
4.調用CGContextDrawPath(),CGContextEOFillPath(),CGContextFillPath()或CGContextStrokePath()函數來填充路徑或繪制路徑邊框即可。在這些方法中,第一個方法可以替換后面的幾個方法,第一個方法指定使用特定的模式來繪制圖形。 它支持如下幾種繪制方式。
- kCGPathFill:指定填充路徑。相當于CGContextFillPath()函數。
- kCGPathEOFill:指定采用even-odd模式填充路徑。相當于CGContextEOFillPath()函數。
- kCGPathStroke:指定只繪制路徑。相當于CGContextStrkePath()函數。
-kCGPathFillStroke:同時繪制路徑、也填充路徑。
- kCGPathEOFillStroke:即繪制路徑,也采用even-odd模式填充路徑。
2.繪制文字
使用CGContextRef繪制文本的步驟如下:
1.獲取繪圖的CGContextRef。
2.設置繪制文本的相關屬性,例如:繪制文本所用的繪制方式、字體大小、字體名稱等。
3.如果只是繪制不需要進行變換的文本,直接調用NSString的drawAtPoint:withAttributes:、drawInAttributes:withFont:等方法繪制即可。如果需要對繪制的文本進行變換,則需要先調用CGContextSetTextMatrix()函數設置變換矩陣,在調用CGContextShowTextAtPoint()方法繪制文本。
3.繪制位圖
為了繪制位圖,UIImage本身已經提供了如下方法。
(1)- drawAtPoint:將該圖片本身繪制到當前繪圖CGContextRef的指定點。調用該方法必須傳入一個CGPoint參數,指定該圖片的繪制點。
(2)- drawAtPoint:blendMode:alpha:屬于前一個方法的增強版,它可以制定繪制圖片的疊加模式和透明度。
(3)- drawInRect:將該圖片本身繪制到當前繪圖CGContextRef的指定區域內。調用該方法必須傳入一個CGRect參數,指定該圖片的繪制區域。
4.設置陰影
CGContextRef為設置圖形陰影提供了如下兩個參數:
(1)- void CGContextSetShadow(CGContextRef context, CGSize offset,CGFloat blur):該函數設置陰影在X、Y方向上的偏移,并設置陰影的模糊程度。該函數的offset包含兩個CGFloat值,第1個CGFloat值控制陰影在X方向的偏移,如果該值為正,則向右偏移,否則向左偏移;第2個CGFloat值控制陰影在Y方向的偏移,如果該值為正,則鄉下偏移,否則向上偏移。最后一個blur參數控制陰影的模糊程度,如果參數為1,表明陰影幾乎不模糊,blur參數越大,陰影越模糊。
示例: CGContextSetShadow(context, CGSizeMake(-8, -6), 5);
(2)- void CGContextSetShadowWithColor(CGContextRef context, CGSize offset,CGFloat blur, CGColorRef color):該函數與前一個函數的功能基本相似,只是該函數多了一個屬性用于設置陰影顏色。
示例: CGContextSetShadowWithColor(context, CGSizeMake(-8, -6), 5, [[UIColor redColor]CGColor]);
現在通過一個案例代碼,來使用以上的方法實現繪制圖片、文字、使用路徑繪制圖片。不過此處繪制圖片卻沒有使用提供的方法,后面說道在內存中繪制位圖時再詳解介紹。
#import "DemoView.h"@implementation DemoView- (void)drawRect:(CGRect)rect { // 獲取繪圖的上下文 CGContextRef context = UIGraphicsGetCurrentContext(); //3.通過路徑的方式創建三角形 [self drawTriangleByPath:context]; //4.繪制文字 [self drawString:context]; //5.繪制圖片 [self drawImage];}-(void)drawImage{ UIImage *image = [UIImage imageNamed:@"6.jpg"]; //將該圖片本身繪制到當前繪圖CGContextRef的指定區域中。 [image drawInRect:CGRectMake(100, 300, 200, 150)]; //將該圖片本身繪制到當前回去CGContextRef的指定點 [image drawAtPoint:CGPointMake(10, 450)];}-(void)drawString:(CGContextRef)context{ NSString *string = @"hello world"; //設置使用描邊模式繪制文字 CGContextSetTextDrawingMode(context, kCGTextStroke); //將文字本身繪制到當前CGContextRef的指定點 [string drawAtPoint:CGPointMake(100, 250) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18],NSForegroundColorAttributeName:[UIColor blueColor]}]; //設置使用填充、描邊繪制文字 CGContextSetTextDrawingMode(context, kCGTextFillStroke); //將文本本身繪制到當前CGContextRef的指定區域中 [string drawWithRect:CGRectMake(200, 250, 70, 80) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18],NSForegroundColorAttributeName:[UIColor blueColor]} context:nil];}-(void)drawTriangleByPath:(CGContextRef)context{ //創建路徑 CGMutablePathRef path = CGPathCreateMutable(); //向路徑中添加圖形 CGPathMoveToPoint(path, NULL, 20, 200);//創建起點 CGPathAddLineToPoint(path, NULL, 100, 300); CGPathAddLineToPoint(path, NULL, 60, 400); //將path添加到上下文 CGContextAddPath(context, path); //閉合路徑 CGContextClosePath(context); //設置繪圖的屬性 //設置描邊顏色 [[UIColor redColor]setStroke]; //設置填充顏色 [[UIColor greenColor]setFill]; //同時設置填充顏色,又能設置描邊顏色// [[UIColor whiteColor]set]; //使用默認的陰影顏色,陰影向左上角投影,模糊度為5// CGContextSetShadow(context, CGSizeMake(-8, -6), 5); CGContextSetShadowWithColor(context, CGSizeMake(-8, -6), 5, [[UIColor redColor]CGColor]); //繪圖 CGContextDrawPath(context, kCGPathFillStroke);}@end
運行效果圖,如下所示:
經過兩個案例,細心地朋友可能發現了這樣的問題:在案例1中對三角形進行設置了線的類型,設置為點線模式,但是矩形的線的類型沒有設置但同樣變成了點線模式。在案例2中對三角形設置了陰影模式,但是圖片、文字也均帶有了陰影。如何解決?
解決辦法,分為兩步走:
1.對上下文進行操作之前,使用 CGContextSaveGState(CGContextRef c)保存當前上下文狀態
2.對上下文進行操作之后,使用 CGContextRestoreGState(CGContextRef c)可以恢復之前保存的上下文狀態
前面介紹的都是 通過擴展UIView、重寫drawRect:方法進行繪圖,這種繪圖方式是直接在UIView控件上繪制所有的圖形 ——由于每次該控件顯示出來時,drawRect:方法都會被調用,這意味著每次該控件顯示出來時,程序都需要重繪所有的圖形,很明顯,這種方式的性能并不好。除此之外,總有些時候需要在內存中繪制圖片,這樣既可導出到手機本地,也可上傳到網絡上。
在內存中繪圖的步驟如下:
(1)調用UIGraphicsBeginImageContext(CGSize size)函數準備繪圖環境。
(2)調用UIGraphiceGetCurrentContext()函數獲取繪圖CGContextRef
(3)用前面介紹的繪制集合圖形、使用路徑等方式進行繪圖。
(4)調用UIGraphicsGetImageFromCurrentImageContext()函數獲取當前繪制的圖形,該方法返回一個UIImage對象。
(5)調用UIGraphicsEndImageContext()函數結束繪圖,并關閉繪圖環境。
#import "ViewController.h"@interface ViewController ()<UIImagePickerControllerDelegate,UINavigationControllerDelegate>@PRoperty (weak, nonatomic) IBOutlet UIImageView *imageView;@property(strong,nonatomic)UIImage *image;@property (weak, nonatomic) IBOutlet UITextField *textField;@end@implementation ViewController- (IBAction)drawClicked:(UIButton *)sender{ //開始圖形繪制的上下文 UIGraphicsBeginImageContext(self.imageView.frame.size); //獲取圖形繪制的上下文 CGContextRef context = UIGraphicsGetCurrentContext(); //繪制矩形 CGContextAddRect(context, CGRectMake(100, 100, 100, 100)); //設置描邊、填充顏色 [[UIColor redColor]set]; //繪制圖形 CGContextDrawPath(context, kCGPathFillStroke); //從圖形繪制上下文獲取圖片 self.image = UIGraphicsGetImageFromCurrentImageContext(); //結束圖形繪制的上下文 UIGraphicsEndImageContext(); //設置顯示圖片 [self.imageView setImage:self.image];}- (IBAction)waterMarkClicked:(UIButton *)sender{ //開始圖形繪制的上下文 UIGraphicsBeginImageContext(self.imageView.frame.size); //先畫圖片// self.image = [UIImage imageNamed:@"6.jpg"]; [self.image drawInRect:self.imageView.bounds]; //再畫水印 NSString *string = self.textField.text; [string drawAtPoint:CGPointMake(100, 100) withAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor],NSFontAttributeName:[UIFont systemFontOfSize:25]}]; //從當前上下文獲取圖片 self.image = UIGraphicsGetImageFromCurrentImageContext(); //結束圖形繪制的上下文 UIGraphicsEndImageContext(); //顯示帶水印的圖片 self.imageView.image = self.image;}- (IBAction)saveClicked:(UIButton *)sender{ //保存圖片到外部設備 /* //設置圖片保存的路徑 NSString *doucuments = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; NSString *fileName = [doucuments stringByAppendingPathComponent:@"watermark.png"]; //獲取圖片中的data NSData *imageData = UIImagePNGRepresentation(self.image); //保存圖片 [imageData writeToFile:fileName atomically:YES]; NSLog(@"%@",NSHomeDirectory()); */ //保存圖片到相冊 UIImageWriteToSavedPhotosAlbum(self.image, nil, nil, nil);}- (IBAction)photoSelect:(UIButton *)sender{ //打開相冊 UIImagePickerController *imagePicker = [[UIImagePickerController alloc]init]; //設置圖片的來源 imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; //設置代理 imagePicker.delegate = self; //使用模態窗口的方式顯示相冊 [self presentViewController:imagePicker animated:YES completion:nil];}#pragma mark - 實現UIImagePickerControllerDelegate代理方法-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ NSLog(@"%@",info); //通過字典方式獲取圖片 self.image = [info objectForKey:UIImagePickerControllerOriginalImage]; [self.imageView setImage:self.image]; //關閉模態窗口 [picker dismissViewControllerAnimated:YES completion:nil];}- (void)viewDidLoad { [super viewDidLoad];}@end
運行效果圖,如下圖:
前面介紹的都是使用一種顏色來填充區域,除此之外,Quartz 2D還允許使用顏色漸變、位圖來填充指定區域。
Quartz 2D為我們提供了兩種漸變填充的函數,分別是: 線性漸變填充、圓形徑向漸變填充。
- void CGContextDrawRadialGradient(CGContextRef context,GradientRef gradient, CGPoint startCenter, CGFloat startRadius,CGPoint endCenter, CGFloat endRadius, CGGradientDrawingOptions options),參數詳解如下:
/* 參數2:gradient參數代表漸變對象,
參數3:startCenter參數設置起始圓的圓心,
參數4:startRadius設置起始圓的半徑,
參數5:endCenter參數代表結束圓的圓心
參數6:endRadius代表結束圓的半徑
參數7:options可支持kCGGradientDrawsBeforeStartLocation(可擴展填充起點之前的區域)或kCGGradientDrawsAfterEndLocation(擴展填充結束點之后的區域)*/
- void CGContextDrawLinearGradient(CGContextRef context,CGGradientRef gradient, CGPoint startPoint, CGPoint endPoint,CGGradientDrawingOptions options):
/* 參數2:gradient參數代表漸變對象,
參數3:startPoint開始點坐標
參數4:endPoint結束點坐標
參數5: options可支持kCGGradientDrawsBeforeStartLocation(可擴展填充起點之前的區域)或kCGGradientDrawsAfterEndLocation(擴展填充結束點之后的區域)*/
廢話不多說,直接上代碼分析:
#import "DemoView.h"@implementation DemoView- (void)drawRect:(CGRect)rect { // 畫漸變:線性漸變、徑向漸變 //獲取繪圖的上下文 CGContextRef context = UIGraphicsGetCurrentContext(); //畫線性漸變// [self drawLinarGradient:context]; [self drawRadialGradient:context];}-(void)drawRadialGradient:(CGContextRef)context{ //2.創建漸變 //2.1創建顏色空間 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); //2.2設置開始顏色、結束時顏色 UIColor *startColor =[UIColor yellowColor]; const CGFloat *startColorCompents = CGColorGetComponents([startColor CGColor]); UIColor *endColor = [UIColor blackColor]; const CGFloat *endColorCompents = CGColorGetComponents([endColor CGColor]); CGFloat components[8] = {startColorCompents[0],startColorCompents[1], startColorCompents[2], startColorCompents[3], endColorCompents[0], endColorCompents[1], endColorCompents[2], endColorCompents[3]}; CGFloat locations[2] = {0.0,1.0}; CGGradientRef gradien = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2); /* 參數詳解; 參數1:用于指定該漸變所使用的顏色空間(如:RGB,CMYK,Gray等顏色空間); 參數2:用于指定根據不同的顏色空間設置多種顏色 參數3:locations參數指定各顏色點得分布位置(如果將該參數指定為NULL,各顏色點將會均勻分布) 參數4:count參數指定該漸變包含多少種顏色 */ //3.繪畫漸變效果(徑向漸變) CGContextDrawRadialGradient(context, gradien, CGPointMake(200, 200), 50, CGPointMake(200, 200), 100, kCGGradientDrawsBeforeStartLocation|kCGGradientDrawsAfterEndLocation); /* 參數2:gradient參數代表漸變對象, 參數3:startCenter參數設置起始圓的圓心, 參數4:startRadius設置起始圓的半徑, 參數5:endCenter參數代表結束圓的圓心 參數6:endRadius代表結束圓的半徑 參數7:options可支持kCGGradientDrawsBeforeStartLocation(可擴展填充起點之前的區域)或kCGGradientDrawsAfterEndLocation(擴展填充結束點之后的區域) */ //4.清理工作 CGColorSpaceRelease(colorSpace); CGGradientRelease(gradien);}-(void)drawLinarGradient:(CGContextRef)context{ //2.創建漸變 //2.1創建顏色空間 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); //2.2設置開始顏色、結束時顏色 UIColor *startColor =[UIColor redColor]; const CGFloat *startColorCompents = CGColorGetComponents([startColor CGColor]); UIColor *endColor = [UIColor blueColor]; const CGFloat *endColorCompents = CGColorGetComponents([endColor CGColor]); CGFloat components[8] = {startColorCompents[0],startColorCompents[1], startColorCompents[2], startColorCompents[3], endColorCompents[0], endColorCompents[1], endColorCompents[2], endColorCompents[3]}; CGFloat locations[2] = {0.0,1.0}; CGGradientRef gradien = CGGradientCreateWithColorComponents(colorSpace, components,NULL, 2); //3.繪畫漸變效果(線性) CGContextDrawLinearGradient(context, gradien, CGPointMake(100, 100), CGPointMake(200, 100),kCGGradientDrawsAfterEndLocation | kCGGradientDrawsBeforeStartLocation); /* options可支持kCGGradientDrawsBeforeStartLocation(可擴展填充起點之前的區域)或kCGGradientDrawsAfterEndLocation(擴展填充結束點之后的區域) */ //4.清理工作 CGColorSpaceRelease(colorSpace); CGGradientRelease(gradien);}@end
運行效果圖,如下所示:
PDF文檔存儲依賴于分辨率的向量圖形、文本和位圖,并用于程序的一系列指令中。一個PDF文檔可以包含多頁的圖形和文本。PDF可用于創建跨平臺、只讀的文檔,也可用于繪制依賴于分辨率的圖形。Quartz為所有應用程序創建高保真的PDF文檔,這些文檔保留應用的繪制操作,如圖下圖所示。PDF文檔的結果將通過系統的其它部分或第三方法的產品來有針對性地進行優化。Quartz創建的PDF文檔在Preview和Acrobat中都能正確的顯示。
案例1:創建pdf文檔(創建并讀取)
#import "ViewController.h"@interface ViewController ()@property (weak, nonatomic) IBOutlet UIWebView *webView;@end@implementation ViewController- (IBAction)showPDF:(id)sender{ //設置pdf文件的路徑 NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSLog(@"%@",documentPath); NSString *pdfFileName = [documentPath stringByAppendingPathComponent:@"face.pdf"]; //創建URL NSURL *url = [NSURL URLWithString:pdfFileName]; //創建request NSURLRequest *request = [NSURLRequest requestWithURL:url]; //在webView中顯示 [self.webView loadRequest:request];}- (void)viewDidLoad { [super viewDidLoad]; // [self createImagePDF];}-(void)createImagePDF{ //設置pdf文件的路徑 NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSLog(@"%@",documentPath); NSString *pdfFileName = [documentPath stringByAppendingPathComponent:@"face.pdf"]; //開始pdf的繪圖上下文 UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectMake(0, 0, 320, 480), nil); for(int i = 0; i < 9; i++) { //開始pdf新的一頁 UIGraphicsBeginPDFPage(); //畫圖像 UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"%d.png",i]]; [image drawAtPoint:CGPointMake(320/2, 480/2)]; } //結束pdf的繪圖上下文 UIGraphicsEndPDFContext();}//創建pdf-(void)createPDF{ //設置pdf文件的路徑 NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSLog(@"%@",documentPath); NSString *pdfFileName = [documentPath stringByAppendingPathComponent:@"test.pdf"]; //開始pdf的繪圖上下文 UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectMake(0, 0, 320, 480), nil); //獲取當前的繪圖上下文 CGContextRef context = UIGraphicsGetCurrentContext(); //開始pdf新的一頁 UIGraphicsBeginPDFPage(); //畫圓形 CGContextAddEllipseInRect(context, CGRectMake(100, 100, 100, 100)); //設置描邊、填充顏色 [[UIColor redColor]set]; //將圖形繪制到上下文中 CGContextDrawPath(context, kCGPathFillStroke); //畫字符串 NSString *str = @"this is a test page."; [str drawAtPoint:CGPointMake(200, 200) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:18],NSForegroundColorAttributeName:[UIColor blueColor]}]; //結束pdf的繪圖上下文 UIGraphicsEndPDFContext();}@end
運行效果圖,如下圖所示:
以上案例中共實現三個功能:
(1)將圖片繪制到pdf文檔中去,
(2)將自定義的圖形繪制到pdf文檔中
(3)通過UIWebView控件進行顯示pdf文檔內容(這種方式只只用于圖形類的內容,如果是電子書文本類的內容,則此方式不可實現)。
#import "PDFView.h"@interface PDFView()<UIActionSheetDelegate,UIAlertViewDelegate>{ CGPDFDocumentRef _pdfDoc; size_t _pageNo;//當前頁碼 size_t _totalPage;//總的頁數}@end@implementation PDFView- (void)drawRect:(CGRect)rect { NSLog(@"重繪"); CGContextRef context = UIGraphicsGetCurrentContext(); //旋轉坐標系 CGContextTranslateCTM(context, 80, self.frame.size.height-60); CGContextScaleCTM(context, 1, -1); //畫頁面 [self drawPDFPage:_pageNo context:context];}//打開pdf文檔-(void)openPDF:(NSURL *)url{ CFURLRef urlRef = (__bridge CFURLRef)url; _pdfDoc = CGPDFDocumentCreateWithURL(urlRef); _totalPage = CGPDFDocumentGetNumberOfPages(_pdfDoc); _pageNo = 1;}//顯示pdf頁面-(void)drawPDFPage:(size_t)pageNo context:(CGContextRef)context{ CGPDFPageRef pdfPage = CGPDFDocumentGetPage(_pdfDoc, pageNo); CGContextDrawPDFPage(context, pdfPage);}- (IBAction)prePage:(id)sender{ if(_pageNo > 1) { _pageNo--; [self setNeedsDisplay]; } }- (IBAction)nextPage:(id)sender{ if(_pageNo < _totalPage) { _pageNo++; [self setNeedsDisplay]; }}@end
運行效果圖,如下所示:
http://www.cocoachina.com/industry/20140115/7703.html
http://www.companysz.com/kenshincui/p/3959951.html
http://www.companysz.com/nngh/p/4797074.html
http://my.oschina.net/joanfen/blog/150411
新聞熱點
疑難解答