UIImage+YYWebImage是YYWebImage(https://github.com/ibireme/YYWebImage)中的一个分类,这个分类封装了一些image常用的变化方法,非常值得学习下源码~(我看的版本是1.0.5)
预备知识:
1,这里大量使用了CoreGraphics的方法,第一个非常常用的的方法就是
UIKIT_EXTERN void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale) NS_AVAILABLE_IOS(4_0);
Parameters
size
-
The size (measured in points) of the new bitmap context. This represents the size of the image returned by the
UIGraphicsGetImageFromCurrentImageContext()
function. To get the size of the bitmap in pixels, you must multiply the width and height values by the value in thescale
parameter. opaque
scale
-
The scale factor to apply to the bitmap. If you specify a value of
0.0
, the scale factor is set to the scale factor of the device’s main screen.
考虑到UIColor是有透明度的,所以这个方法传入的参数opaque一般传NO;考虑到屏幕的的Scale,所以传入的scale参数一般传0,或者self.scale;
文档有写“This function may be called from any thread of your app.” 所以在任何线程做都没有问题~~
具体方法:
1, + (UIImage *)yy_imageWithColor:(UIColor *)color size:(CGSize)size;生成一个指定大小的纯色图片;
方法实现比较简单,就是创建一个图片上下文,拿到这个上下文,设置其为指定颜色,填充,取到图片,关闭这个图片上下文,就可以了;
如最开始所说,opaque是NO,scale是0;
如果是用来做背景用的图片,可以直接将size设置为(1,1),然后用系统默认的拉伸效果来把图片放大( UIViewContentModeScaleToFill),这样效率比较高;
YYImage也提供了 + (nullable UIImage *)yy_imageWithColor:(UIColor *)color;这个方法来实现这个功能。
2, - (void)yy_drawInRect:(CGRect)rect withContentMode:(UIViewContentMode)contentMode clipsToBounds:(BOOL)clips;
这个方法我感觉应该算是个内部方法,个人感觉没有必要放出来,可能有场景会用到我没遇到过吧;
3, - (nullable UIImage *)yy_imageByResizeToSize:(CGSize)size contentMode:(UIViewContentMode)contentMode;按指定的模式生成一个指定大小的图片;size就是生成的图片的大小,多出来的地方是透明的;
这个方法是调用- (void)yy_drawInRect:(CGRect)rect withContentMode:(UIViewContentMode)contentMode clipsToBounds:(BOOL)clips;来实现的。里面用到的 static CGRect _YYCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode)这个方法得值得学习下。这个方法是用来得到按模式放大或者缩小后图片完整的rect,比如:
一张100*200的图片,用 UIViewContentModeScaleAspectFit的模式,放到一个400*400的imageView中,那得到的rect就是(100,0,200,400);
一张100*200的图片,用 UIViewContentModeScaleAspectFill的模式,放到一个400*400的imageView中,那得到的rect就是(0,200,400,800);
。。。。。。
不同的模式会得到不同的rect,生成的图片也会不一样;
具体rect的计算是根据图片的宽高比和目标rect的宽高比来确定的,而不是具体的宽或者高,这个需要特别注意,这完全是为了达到缩放模式的效果而做的数学计算。这里真的很佩服作者!!!
- (UIImage *)yy_imageByResizeToSize:(CGSize)size;没有调用这个方法,而是另外写了一遍,效果跟用这个方法contentModel传 UIViewContentModeScaleToFill是一样的,不知道有啥用意~
4, - (nullable UIImage *)yy_imageByCropToRect:(CGRect)rect;裁剪图片中的某个区域
注意裁剪范围只跟image的size有关,跟imageView的大小无关;
如果rect比image的size大,则没有效果,如果rect比image的size小,会剪裁出指定的区域;
5, - (nullable UIImage *)yy_imageByInsetEdge:(UIEdgeInsets)insets withColor:(nullable UIColor *)color;根据insets来剪裁图片;
这个方法也很牛X~,注意insets是可以取负数的,如果取负数,就相当与给原来的图片加上指定颜色的边框;
如果为正数,就是根据insets来剪裁图片,这时候color参数无效
6, - (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius
corners:(UIRectCorner)corners
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor
corners:(UIRectCorner)corners
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor
borderLineJoin:(CGLineJoin)borderLineJoin;
设置圆角的方法
里面用了 CGContextDrawImage(context, rect, self.CGImage);的方法;而CGContext的坐标系与UIKit的坐标系是不同的,所以需要特殊处理下;
这里他用了修改坐标系的方法来解决,所以corners也是颠倒的,需要特殊处理下;
注意这个方法只会设置圆角,如果原来的图片不是正方形的,那怎么设置页不会得到正圆的图片,需要预先把图片裁成正方形的才行;
7, - (nullable UIImage *)yy_imageByRotate:(CGFloat)radians fitSize:(BOOL)fitSize;生成旋转指定角度后的图片
fitSize传yes,图片会自动压缩使图片显示完整,传no图片会被截断;
其中 CGRectApplyAffineTransform返回的是“包含旋转后的rect的最小矩形的CGRect”,origin的值可能为负值;
这个方法使用 CGBitmapContextCreate这个方法来绘制的,跟前面的直接用 UIGraphicsGetCurrentContext不太一样,反正我是不太懂。。。
我能看懂的就这么几句:
CGContextTranslateCTM(context, +(newRect.size.width * 0.5), +(newRect.size.height * 0.5));
CGContextRotateCTM(context, radians);
CGContextDrawImage(context, CGRectMake(-(width * 0.5), -(height * 0.5), width, height), self.CGImage);
CGImageRef imgRef = CGBitmapContextCreateImage(context);
UIImage *img = [UIImageimageWithCGImage:imgRef scale:self.scaleorientation:self.imageOrientation];
CGContextDrawImage(context, CGRectMake(-(width * 0.5), -(height * 0.5), width, height), self.CGImage);
CGImageRef imgRef = CGBitmapContextCreateImage(context);
UIImage *img = [UIImageimageWithCGImage:imgRef scale:self.scaleorientation:self.imageOrientation];
CGImageRelease(imgRef);
这里比较巧妙,貌似CGContext的锚点是在(0,0)的,而image的锚点是在中心点的,所以先移动context到中心点,再从(-(width * 0.5), -(height * 0.5))开始绘图,然后得到CGImageRef,用[UIImageimageWithCGImage:imgRef scale:self.scaleorientation:self.imageOrientation]这个方法来修正坐标系的问题,也是非常值得学习~
8,- (nullable UIImage *)yy_imageByBlurRadius:(CGFloat)blurRadius
tintColor:(nullable UIColor *)tintColor
tintMode:(CGBlendMode)tintBlendMode
saturation:(CGFloat)saturation
maskImage:(nullable UIImage *)maskImage;
图片模糊效果
好用,但实现超出了目前的我的理解范围,待研究。。。
9, + (nullableUIImage *)yy_imageWithSmallGIFData:(NSData *)data scale:(CGFloat)scale;
文档说是小型gif用,比如表情~实现没有看懂,待研究。。。