ios开发绘制,iOS开发绘制渐变色view

iOS 图形绘制方案

先说下背景,项目里需要绘制音乐和视频的波形图,由于产品上的设计,波形图的长度基本都可以达到屏幕长度的几十倍。并且图形并不是折线图而是柱状图,还要跟随音乐音量变化,所以图形肯定是无法直接拉伸挤压的,所以当时为了性能和内存方面的考虑,尝试了很多方案。

创新互联建站坚持“要么做到,要么别承诺”的工作理念,服务领域包括:网站设计制作、成都网站设计、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的丹棱网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

UIImage:

使用UIGraphicsGetImageFromCurrentImageContext方法将绘制的图形生成图片。

这种方式适用于图片不长并且图片不变的情况。

优点:可在子线程绘制,方便缓存。

缺点:占用内存大,绘制不够高效。

PS: 注意此方法有个隐患,因为系统会对设置给UIImageView的图片进行缓存,如果一直调用,即使是完全相同的图片,也会产生内存占用。

示例:

CALayer :

layer的基类,重写drawInContext方法进行进行绘制。

优点:量级轻(实在想不到优点)。

缺点:主线程绘制,绘制不够高效 。

示例:

CATiledLayer:

layer的子类,专门用于绘制大图的方案,系统底层已进行过优化,子线程绘制,并且不会绘制屏幕外的内容。可将大图分割成若干个更小的单元进行绘制,可通过tileSize设置单个绘制单元的大小。

优点:子线程绘制,性能极好。

缺点:绘制较缓慢,能控制的变量较少。

示例:

与CALayer用法一致,可设置额外属性

YYAsyncLayer:

知名的异步绘制的第三方控件,是CALayer的子类,内部创建了队列进行管理,能将绘制的操作转换为异步操作,并且引入了RunLoop机制进行管理,只在RunLoop空闲的时候才进行刷新操作。

优点:子线程绘制,性能很高,不阻塞用户操作。

缺点:因为只在空闲时执行,图形刷新不及时。

示例:

CAShapeLayer + UIBezierPath:

layer的子类,CAshapeLayer能在GPU上渲染,性能很高,绘制速度很快,而且曲线绘制既能选择在主线程绘制,也能选择在子线程绘制。

优点:无论子线程还是主线程都可绘制,且性能很高。

缺点:曲线路径量大的话,还是影响性能。

示例:

最终还是选择了CAShapeLayer + UIBezierPath方案,但是因为音波数据量特别大(每秒40个音频数据),导致多个图形频繁刷新的时候发热十分严重,而且也出现了卡顿的情况。

为了优化,最后又加入了分屏绘制的逻辑:

蓝色区域表示屏幕区域,红色表示绘制的区域,黑色线条表示临界边,

整体逻辑:

0、转换坐标为窗体坐标。

1、判断是否有上次绘制的位置,没有则直接绘制。

2、绘制完成后保存当前位置为绘制位置,计算出黑色临临界区域。

3、滑动视图的过程中判断滑动位置是否超出了黑线区域,超出则重新进行绘制。

4、重复2、3。

iOS开发的动态曲线图绘制

最近项目中需要用到曲线图,虽然有很多demo,但还是想自己写个,毕竟也不难,当然效果不如网上那些大神的好看~毕竟水平有限,但是也足够我应付项目需求了嘿嘿(主要还是闲的,哈哈)

首先效果如图:

1.首先自定义一个view,我定义了这些属性

(忽略我蹩脚的起名)

2.开始画图 首先根据x坐标的个数画出表格中的竖线及坐标刻度

依葫芦画瓢得到众横线

接着根据实际值在表格中划出红点及实际坐标值

其中以下是两个懒加载

自定义的初始化方法:

动态连接各个点,我让这个行为在️秒内执行完

大功告成,直接就可以调用啦

demo地址:

[img]

iOS 图形绘制(二)-UIBezierPath

UIBezierPath 可以创建基于矢量的路径,例如椭圆或者矩形,或者有多个直线和曲线段组成的形状。

使用 UIBezierPath ,你只能在当前上下文中绘图,所以如果你当前处于 UIGraphicsBeginImageContextWithOptions 函数或 drawRect: 方法中,你就可以直接使用UIKit提供的方法进行绘图。如果你持有一个 context: 参数,那么使用UIKit提供的方法之前,必须将该上下文参数转化为当前上下文。幸运的是,调用 UIGraphicsPushContext 函数可以方便的将 context: 参数转化为当前上下文,记住最后别忘了调用 UIGraphicsPopContext 函数恢复上下文环境。

简言之:我们一般使用 UIBezierPath 都是在重写的 drawRecrt: 方法这种情形。其绘图的步骤是这样的:

1.重写 drawRect: 方法。但不需要我们自己获取当前上下文 context ;

2.创建相应图形的 UIBezierPath 对象,并设置一些修饰属性;

3.渲染,完成绘制。

绘制矩形最简单的办法是使用 UIRectFrame 和 UIRectFill

通过使用 UIBezierPath 可以自定义绘制线条的粗细,是否圆角等。

多边形是一些简单的形状,这些形状是由一些直线线条组成,我们可以用 moveToPoint: 和 addLineToPoint: 方法去构建。 moveToPoint: 设置我们想要创建形状的起点。从这点开始,我们可以用方法 addLineToPoint: 去创建一个形状的线段。

我们可以连续的创建 line,每一个 line 的起点都是先前的终点,终点就是指定的点。

closePath 可以在最后一个点和第一个点之间画一条线段。

想画弧线组成的不规则形状,我们需要使用中心点、弧度和半径,如下图。弧度使用顺时针脚底,0弧度指向右边,pi/2指向下方,pi指向左边,-pi/2指向上方。然后使用 bezierPathWithArcCenter: radius: startAngle endAngle: clockwise: 方法来绘制。

iOS进阶--UIView的绘制

如果要研究OpenGL ES相关和 GPU 相关,这篇文章很具有参考的入门价值.

首先要从 Runloop 开始说,iOS 的 MainRunloop 是一个60fps 的回调,也就是说16.7ms(毫秒)会绘制一次屏幕,这个时间段内要完成:

这些 CPU 的工作.

然后将这个缓冲区交给 GPU 渲染, 这个过程又包含:

最终现实在屏幕上.因此,如果在16.7ms 内完不成这些操作, eg: CPU做了太多的工作, 或者 view 层次过于多,图片过于大,导致 GPU 压力太大,就会导致"卡"的现象,也就是 丢帧 , 掉帧 .

苹果官方给出的最佳帧率是: 60fps (60Hz),也就是一帧不丢, 当然这是理想中的绝佳体验.

一般来说如果帧率达到 60+fps (fps = 60帧以上,如果帧率fps 50,人眼就基本感觉不到卡顿了,因此,如果你能让你的 iOS 程序 稳定 保持在 60fps 已经很不错了, 注释,是"稳定"在60fps,而不是, 10fps , 40fps , 20fps 这样的跳动,如果帧频不稳就会有卡的感觉, 60fps 真的很难达到, 尤其是在 iPhone 4/4s等 32bit 位机上,不过现在苹果已经全面放弃32位,支持最低64位会好很多.

总的来说, UIView从绘制到Render的过程有如下几步:

UIView 的绘制和渲染是两个过程:

上面提到的从 CPU 到 GPU 的过程可用下图表示:

下面具体来讨论下这个过程

假设我们创建一个 UILabel

这个时候不会发生任何操作, 由于 UILabel 重写了 drawRect 方法,因此,这个 View 会被 marked as "dirty" :

类似这个样子:

然后一个新的 Runloop 到来,上面说道在这个 Runloop 中需要将界面渲染上去,对于 UIKit 的渲染,Apple用的是它的 Core Animation 。 做法是在Runloop开始的时候调用:

在 Runloop 结束的时候调用

在 begin 和 commit 之间做的事情是将 view 增加到 view hierarchy 中,这个时候也不会发生任何绘制的操作。 当 [CATransaction commit] 执行完后, CPU 开始绘制这个 view :

首先 CPU 会为 layer 分配一块内存用来绘制 bitmap ,叫做 backing store

创建指向这块 bitmap 缓冲区的指针,叫做 CGContextRef

通过 Core Graphic 的 api ,也叫 Quartz2D ,绘制 bitmap

将 layer 的 content 指向生成的 bitmap

清空 dirty flag 标记

这样 CPU 的绘制基本上就完成了.

通过 time profiler 可以完整的看到个过程:

假如某个时刻修改了 label 的 text :

由于内容变了, layer 的 content 的 bitmap 的尺寸也要变化,因此这个时候当新的 Runloop 到来时, CPU 要为 layer 重新创建一个 backing store ,重新绘制 bitmap .

CPU 这一块最耗时的地方往往在 Core Graphic 的绘制上,关于 Core Graphic 的性能优化是另一个话题了,又会牵扯到很多东西,就不在这里讨论了.

GPU bound:

CPU 完成了它的任务:将 view 变成了 bitmap ,然后就是 GPU 的工作了, GPU 处理的单位是 Texture .

基本上我们控制 GPU 都是通过 OpenGL 来完成的,但是从 bitmap 到 Texture 之间需要一座桥梁, Core Animation 正好充当了这个角色:

Core Animation 对 OpenGL 的 api 有一层封装,当我们要渲染的 layer 已经有了 bitmap content 的时候,这个 content 一般来说是一个 CGImageRef , CoreAnimation 会创建一个 OpenGL 的 Texture 并将 CGImageRef(bitmap) 和这个 Texture 绑定,通过 TextureID 来标识。

这个对应关系建立起来之后,剩下的任务就是 GPU 如何将 Texture 渲染到屏幕上了。 GPU 大致的工作模式如下:

整个过程也就是一件事:

CPU 将准备好的 bitmap 放到 RAM 里, GPU 去搬这快内存到 VRAM 中处理。 而这个过程 GPU 所能承受的极限大概在16.7ms完成一帧的处理,所以最开始提到的60fps其实就是GPU能处理的最高频率.

因此, GPU 的挑战有两个:

这两个中瓶颈基本在第二点上。渲染 Texture 基本要处理这么几个问题:

Compositing 是指将多个纹理拼到一起的过程,对应 UIKit ,是指处理多个 view 合到一起的情况,如:

如果 view 之间没有叠加,那么 GPU 只需要做普通渲染即可.

如果多个 view 之间有叠加部分, GPU 需要做 blending .

加入两个 view 大小相同,一个叠加在另一个上面,那么计算公式如下:

R = S + D *( 1 - Sa )

其中 S , D 都已经 pre-multiplied 各自的 alpha 值。

Sa 代表 Texture 的 alpha 值。

假如 Top Texture (上层 view )的 alpha 值为 1 ,即不透明。那么它会遮住下层的 Texture .

即, R = S 。是合理的。

假如 Top Texture (上层 view )的 alpha 值为 0.5 ,

S 为 (1,0,0) ,乘以 alpha 后为 (0.5,0,0) 。

D 为 (0,0,1) 。

得到的 R 为 (0.5,0,0.5) 。

基本上每个像素点都需要这么计算一次。

因此, view 的层级很复杂,或者 view 都是半透明的( alpha 值不为 1 )都会带来 GPU 额外的计算工作。

这个问题,主要是处理 image 带来的,假如内存里有一张 400x400 的图片,要放到 100x100 的 imageview 里,如果不做任何处理,直接丢进去,问题就大了,这意味着, GPU 需要对大图进行缩放到小的区域显示,需要做像素点的 sampling ,这种 smapling 的代价很高,又需要兼顾 pixel alignment 。 计算量会飙升。

如果我们对 layer 做这样的操作:

会产生 offscreen rendering ,它带来的最大的问题是,当渲染这样的 layer 的时候,需要额外开辟内存,绘制好 radius,mask ,然后再将绘制好的 bitmap 重新赋值给 layer 。

因此继续性能的考虑, Quartz 提供了优化的 api :

简单的说,这是一种 cache 机制。

同样 GPU 的性能也可以通过 instrument 去衡量:

红色代表 GPU 需要做额外的工作来渲染 View ,绿色代表 GPU 无需做额外的工作来处理 bitmap 。

全文完

收录: 原文地址

iOS开发 ,将多张图片叠加绘制到一起

这种绘制是根据图片的像素比例 等比例进行绘制的,在选择图片和创建展示图片的imageView 时,注意查看尺寸 注:绘图时使用  [UIScreen mainScreen].scale 可以是图片更清晰 UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);//这样就不模糊了

//图片上添加文字 详细版

- (UIImage*)text:(NSString*)text addToView:(UIImage*)image{

//设置字体样式

UIFont*font = [UIFont fontWithName:@"Arial-BoldItalicMT"size:100];

NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:text];

[str addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(0, 1)];

[str addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:100] range:NSMakeRange(0, text.length)];

//    CGSize textSize = [text sizeWithAttributes:dict];

CGSize textSize = [str size];

//绘制上下文

UIGraphicsBeginImageContext(image.size);

//UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);//这样就不模糊了

[image drawInRect:CGRectMake(0,0, image.size.width, image.size.height)];

//    int border =10;

CGRect re = {CGPointMake((image.size.width- textSize.width)/2, 200), textSize};

//    CGRect rect = CGRectMake(0, 0, image.size.width, 500);

//此方法必须写在上下文才生效

[str drawInRect:re ];

UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return newImage;

}

//修改图片尺寸

- (UIImage*)imageWithImageSimple:(UIImage*)image scaledToSize:(CGSize)newSize

{

// Create a graphics image context

UIGraphicsBeginImageContext(newSize);//这样压缩的图片展示出来会很模糊

//UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);//这样就不模糊了

//UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);//这样就不模糊了

// Tell the old image to draw in this new context, with the desired

// new size

[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];

// Get the new image from the context

UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();

// End the context

UIGraphicsEndImageContext();

// Return the new image.

return newImage;

}

//圆角

- (UIImage *) getRadioImaeg:(NSString *)imageName1{

UIImage *image1 = [UIImage imageNamed:imageName1];

UIGraphicsBeginImageContextWithOptions(image1.size, 0, 0);

CGContextRef ctx = UIGraphicsGetCurrentContext();

CGRect rect = CGRectMake(00, 0, image1.size.width, image1.size.width);

CGContextAddEllipseInRect(ctx, rect);

CGContextClip(ctx);

[image1 drawInRect:rect];

UIImage *img = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return img;

}

//图片叠加

- (UIImage *)addImage:(NSString *)imageName1 withImage:(NSString *)imageName2 {

UIImage *image1 = [UIImage imageNamed:imageName1];

UIImage *image2 = [self getRadioImaeg:@"333"];

UIGraphicsBeginImageContext(image1.size);

//UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);//这样就不模糊了

[image1 drawInRect:CGRectMake(0, 0, image1.size.width, image1.size.height)];

[image2 drawInRect:CGRectMake((image1.size.width - image2.size.width)/2,(image1.size.height - image2.size.height)/2, image2.size.width, image2.size.height)];

UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return resultingImage;

}


分享文章:ios开发绘制,iOS开发绘制渐变色view
本文URL:http://pwwzsj.com/article/dsohohe.html