背景
.9图来源于Android。为了设计出一套图,兼容Android和iOS,使用.9图的方式来对图片进行拉伸以适应不同的屏幕。在iOS中没有.9图的概念,只能先了解Android的.9图再进行模拟Android的方法。
什么是.9图片
即图片后缀名前有.9的图片,如pic.9.png、pic1.9.jgp,诸如此类的图片就称为.9图片。且在原始图片四周添加了一个像素,在这一个像素上用黑线标识出相关的区域。
.9图片的作用
.9图片的作用是在图片拉伸的时候特定的区域不会发生图片失真,特定的区域就是由.9图边缘一圈像素中的left和top的黑线所决定。
Android中的.9图介绍
.9图其实是在原始图片的四周添加了一个像素,通俗点说就是图片比原始图片扩大了一圈,多出来的那一圈是透明像素。我们需要绘制黑线的地方正是在那一圈透明像素上进行绘制。
如上图所示,在原始图的四周添加了一个圈的透明像素。其中:四周分别命名为L、T、R、B。
绘制在L的区域:用于拉伸的纵向区域。
绘制在T的区域:用于拉伸的横向区域。
绘制在R的区域:用于显示前景的纵向范围。
绘制在B的区域:用于显示前景的横向范围。
举例说明一下,如下图 :
该图的四周添加了相应的黑线。
拉伸区域,如下图:
红色框区域:表示纵向拉伸的区域,也就是说,当图片需要纵向拉伸的时候它会只指定拉伸红色区域,其他区域在纵向是不会拉伸的。
绿色框区域:表示横向拉伸的区域,也就是说,当图片需要横向拉伸的时候它会只指定拉伸绿色区域,其他区域在横向是不会拉伸的。
显然红色和绿色相交的部分是既会进行横向拉伸也会进行纵向拉伸的。
前景的显示区域,如下图:
蓝色区域:表示前景能显示的纵向范围。即前景的最上面可以显示到什么地方,最下面可以显示的什么地方。
黄色区域:表示前景能显示的横向范围。即前景的最左边可以显示到什么地方,最右边可以显示的什么地方。
蓝色和黄色相交部分:表示整个前景能显示的区域。一个区域是矩形的,蓝色规定了上下边界,黄色规定了左右边界,两者共同当然也就规定了一个矩形区域。
在iOS中图片拉伸:
1.在iOS5之前,使用- (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight;这个方法进行图片的拉伸。
2.在iOS5之后,使用- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets;
或- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets resizingMode:(UIImageResizingMode)resizingMode;这个方法进行图片的拉伸或平铺。
iOS中有个叫端盖(end cap)的概念,用来指定图片中的哪一部分不用拉伸。比如下图中,黑色代表需要被拉伸的矩形区域,上下左右不需要被拉伸的边缘就称为端盖。
如上所述,在iOS中想要拉伸图片,就得确定要被拉伸的区域,而这个区域是由top、left、bottom、right四个方向的值所决定的区域,这部分的概念与Android中的.9图是不太一样的,但拉伸的区域概念差不太多。
了解了以上概念,我们就可以在iOS中模拟Android处理.9图的拉伸模式。
我们使用了一个第三方库:LVNinePatchImage进行.9图的处理。因为原始图片四周添加了一像素的透明。而只有黑线的alpha值是为1的,其余部分都是透明的则alpha值为0。通过这种逻辑关系就能找到对应的左边和上边的黑线区域位置。然后通过iOS系统提供的拉伸方法进行图片的拉伸操作。
该库主要主要做的事有:
1.通过类方法+ (unsigned char*)getRGBAsFromImage:(UIImage*)image count:(NSInteger)count;来获取图片的所有像素点的RGB值包括Alpha值。
2.通过方法 inline static CGFloat getImageAphaAtPoint(const unsigned char* rawData, NSInteger width, NSInteger height , NSInteger pointX, NSInteger pointY);去找出pointX到pointY之间像素点的Alpha值。
如当pointY始终为0,pointX从1到图片的宽度,就能找出图片顶部一像素的所有alpha值。当pointX始终为0,pointY从1到图片的高度,就能找出图片左边一像素的所有alpha值。
3.然后遍历找到的顶部和左边的alpha数组值,当alpha为1的连续区域就是黑线的所在位置。
4.然后调用方法+ (UIImage*)crop:(CGRect)r image0:(UIImage*)image0;裁剪掉图片四周的一像素区域,只保留中间正确(最原始)的图片。
5.最后再根据黑线的所在位置,算出top、left、bottom、right端盖的值,调用- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets;方法进行图片的拉伸或平铺。
参考链接
http://blog.****.net/shimiso/article/details/41079847
http://www.cnblogs.com/mjios/archive/2013/02/26/2934280.html