目 录:web
1、基础知识掌握数组
2、Quartz 2D绘图基础:CGContextRef实现简单地绘制图形网络
3、CGContextRef实现文字、图片、基于路径的图形绘制框架
4、在内存中绘制位图函数
5、添加渐变效果性能
6、PDF文档学习
引言:Quartz 2D绘图的核心API是CGContextRef,该API专门用于绘制各类图形。Quartz 2D是一个二维图形绘制引擎,它支持iOS环境和Mac OS X环境,为开发者会提供了不少的方便,它在绘图上功能是很是强大的,如基于路径的绘图、透明度、阴影、颜色管理、反锯齿、PDF文档生成和PDF元数据访问等。Quartz 2DAPI做为Core Graphics框架的一部分,所以其中的不少数据类型和方法都是以CG开头的。会常常见到Quartz 2D(Quartz)和Core Graphics两个术语交互使用。字体
1、基础知识掌握优化
学习Quartz 2D以前首先先来掌握几个基础知识。this
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方法返回的图形上下文就是用的是这种坐标系(坐标系统的原点位于左上角)
1)原点(0,0)在屏幕的左上角,X轴向右正向延伸,Y轴向下正向延伸
2)iOS的像素分辨率会随设备的硬件而变化,iPhone4第一次引入了视网膜屏幕,像素分辨率为960* 640,恰好是前一代iPod和iPhone像素分辨率( 480* 320)的两倍
3)在绘图时,须要使用“点”的概念来思考问题,而不是像素。也就是说在点坐标系中绘图,不是硬件的像素坐标系
4)虽然这些设备的像素分辨率不一样,但用到的坐标系保持不变(以点为单位)。在iPhone4上,一个点会用2像素宽度来绘制
提示:若是绘图的上下文,是使用UIGraphicsGetCurrentContext或者其余以UI开头的方法获取到的,在绘图时无需进行坐标转换
4.Quartz 2D的绘图顺序
后面绘制的图形,会覆盖先前绘制的图形。以下图所示:
2、Quartz 2D绘图基础:CGContextRef实现简单地绘制图形
使用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值依次控制点线的实现长度、间距。好比该参数以下:
前面说到CGContextRef不但能提供绘制基本图形的功能,还能够提供绘制文字、图片、基于路径的图形的绘制。下面来看下对这三种的绘制时Quartz 2D是如何实现的。
1.使用路径
使用路径的步骤以下:
#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)能够恢复以前保存的上下文状态
4、在内存中绘制位图
前面介绍的都是经过扩展UIView、重写drawRect:方法进行绘图,这种绘图方式是直接在UIView控件上绘制全部的图形——因为每次该控件显示出来时,drawRect:方法都会被调用,这意味着每次该控件显示出来时,程序都须要重绘全部的图形,很明显,这种方式的性能并很差。除此以外,总有些时候须要在内存中绘制图片,这样既可导出到手机本地,也可上传到网络上。
#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
运行效果图,以下图:
5、添加渐变效果
前面介绍的都是使用一种颜色来填充区域,除此以外,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
运行效果图,以下所示:
6、PDF文档
1.绘制、读取PDF文档
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文档内容(这种方式只只用于图形类的内容,若是是电子书文本类的内容,则此方式不可实现)。
2.使用Quartz 2D的方式读取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
运行效果图,以下所示: