移動(dòng)應(yīng)用中圖像處理對(duì)于用戶體驗(yàn)來說至關(guān)重要,也是考驗(yàn)app性能、網(wǎng)絡(luò)速度的重要指標(biāo)岗屏。
在開發(fā)和使用app的過程中默蚌,ScrollView經(jīng)常作為UIImageView的載體冻晤,在滑動(dòng)過程中Image是否
流暢加載和顯示是移動(dòng)開發(fā)中最基本也是最常見的優(yōu)化場(chǎng)景。下面就從一些最基本的方向來
總結(jié)一下Image的處理套路绸吸。
圓角
從很多網(wǎng)絡(luò)資料上都可以看到對(duì)于UIImageView的圓角設(shè)置會(huì)導(dǎo)致離屏渲染鼻弧,從而損傷性能设江,
那我們?cè)陂_發(fā)中應(yīng)該怎樣去平衡性能損失和編碼復(fù)雜度呢?一般情況下給UIImageView設(shè)置
圓角都是通過如下兩行代碼實(shí)現(xiàn):
imageView.layer.cornerRadius = 5
imageView.layer.masksToBounds = true
這兩行代碼很簡(jiǎn)單攘轩,通過設(shè)置UIView的layer層來實(shí)現(xiàn)圓角叉存,在頁(yè)面圖片圓角不多的情況下,
這就是最簡(jiǎn)單的方式度帮,也不會(huì)帶來多少性能損耗鹉胖。
如果是頁(yè)面圖片圓角較多的話,如上操作會(huì)帶來嚴(yán)重的性能問題够傍,故不可取甫菠。此時(shí)可行的處理
方法就是直接截取圖片實(shí)現(xiàn):
- (UIImage *)imageWithSize:(CGSize)size cornerRadius:(CGFloat)cornerRadius {
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, size.width, size.height) cornerRadius:cornerRadius] addClip];
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
直接將截取好的圖片填入U(xiǎn)IImageView中,所以對(duì)于移動(dòng)App中feed流這種需要好的體驗(yàn)的地方冕屯,
一般是采取的這種方式寂诱。當(dāng)然對(duì)于普通的畫圖,我們也可以使用Core Graphics這樣來實(shí)現(xiàn):
+ (UIImage *)imageWithFillColor:(UIColor *)fillColor strokeColor:(UIColor *)strokeColor size:(CGSize)size lineWidth:(CGFloat)lineWidth cornerRadius:(CGFloat)radius {
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
[fillColor setFill];
[strokeColor setStroke];
CGRect roundedRect = CGRectMake(lineWidth/2, lineWidth/2, size.width-lineWidth, size.height-lineWidth);
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:roundedRect cornerRadius:radius];
path.lineWidth = lineWidth;
[path fill];
[path stroke];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
當(dāng)然我們可以重寫一個(gè)UIView的drawRect方法來得到不同形狀的UIView安聘,但是調(diào)用drawRect
方法也會(huì)導(dǎo)致離屏渲染痰洒,實(shí)不可取。用上面的繪圖方法繪出特定的圖形浴韭,然后代替UIView才
是正確高效的實(shí)現(xiàn)方式丘喻。
主題色
每個(gè)app都有自己特有的tintColor,最明顯的就是當(dāng)我們選中一個(gè)TabBarItem的時(shí)候念颈,圖標(biāo)
會(huì)變色泉粉,這種情況使用tintColor作為圖標(biāo)的選中色最合適不過了,那么我們?cè)鯓拥玫絫intColor
背景的圖片呢榴芳,可以為UIImage寫一個(gè)擴(kuò)展為UIImage+Tint.h嗡靡,實(shí)現(xiàn)如下:
@implementation UIImage (Tint)
- (UIImage *) imageWithTintColor:(UIColor *)tintColor
{
return [self imageWithTintColor:tintColor blendMode:kCGBlendModeDestinationIn];
}
- (UIImage *) imageWithGradientTintColor:(UIColor *)tintColor
{
return [self imageWithTintColor:tintColor blendMode:kCGBlendModeOverlay];
}
- (UIImage *) imageWithTintColor:(UIColor *)tintColor blendMode:(CGBlendMode)blendMode
{
//We want to keep alpha, set opaque to NO; Use 0.0f for scale to use the scale factor of the device’s main screen.
if (tintColor) {
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
[tintColor setFill];
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
UIRectFill(bounds);
//Draw the tinted image in context
[self drawInRect:bounds blendMode:blendMode alpha:1.0f];
if (blendMode != kCGBlendModeDestinationIn) {
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f];
}
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
else {
return self;
}
}
@end
上面也是通過Core Graphics來為圖片加載主題色的,至于代碼中的blendMode窟感,渲染模式對(duì)
透明度和漸變色產(chǎn)生影響讨彼,除非你對(duì)這個(gè)參數(shù)非常了解,否則請(qǐng)使用上面的方式柿祈。
透明度
設(shè)置一張圖片或者UIView的透明度對(duì)于性能也會(huì)產(chǎn)生影響哈误,設(shè)置圖片透明度的正確姿勢(shì)為:
- (UIImage *)imageByApplyingAlpha:(CGFloat) alpha {
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -area.size.height);
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextSetAlpha(ctx, alpha);
CGContextDrawImage(ctx, area, self.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
大小
很多時(shí)候我們會(huì)去單純地改變一張圖片的大小,而且這種要求會(huì)很頻繁躏嚎,這里也是推薦使用
Core Graphics來改變一張圖片的大忻圩浴:
- (UIImage *)resizeImageWithMaxSize:(CGSize)maxSize {
if (maxSize.width < FLT_EPSILON || maxSize.height < FLT_EPSILON) {
return nil;
}
CGSize size = self.size;
if (size.width < maxSize.width && size.height < maxSize.height) {
return self;
}
CGFloat widthRatio = maxSize.width / size.width;
CGFloat heightRatio = maxSize.height / size.height;
CGFloat ratio = widthRatio < heightRatio ? widthRatio : heightRatio;
CGSize finalSize = CGSizeMake(size.width * ratio, size.height * ratio);
UIGraphicsBeginImageContext(finalSize);
[self drawInRect:CGRectMake(0, 0, finalSize.width, finalSize.height)];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resizedImage;
}
緩存
當(dāng)圖片需要重復(fù)使用的時(shí)候,我們有必要對(duì)于圖像進(jìn)行緩存紧索,這樣能夠消除CPU的重復(fù)圖像
處理時(shí)間袁辈,在進(jìn)行圖像緩存的時(shí)候菜谣,使用最簡(jiǎn)單的NSMutableDictionary就行珠漂,緩存的Value
是UIImage晚缩,Key為圖像的四個(gè)基本屬性(name、UIImageTransformMode媳危、size和cornerRadius)
當(dāng)然荞彼,并不是這四個(gè)屬性都是必須,不過一般情況下以name和size作為key待笑。
+ (UIImage *)imageSVGNamed:(NSString *)name transformMode:(LNImageTransformMode)mode size:(CGSize)size cornerRadius:(CGFloat)cornerRadius cache:(BOOL)needCache {
NSDictionary *cacheKey = @{@"name" : name, @"mode": @(mode), @"size": [NSValue valueWithCGSize:size], @"cornerRadius": @(cornerRadius)};
UIImage *image = [[SVGImageCache sharedImageCache] cachedImageWithKey:cacheKey];
if (image == nil) {
image = [[UIImage svg2Image:name size:size] imageWithTransformMode:mode size:size cornerRadius:cornerRadius];
if (image && needCache) {
[[SVGImageCache sharedImageCache] addImageToCache:image forKey:cacheKey];
}
}
return image;
}
格式
判斷圖像的格式很容易鸣皂,讀取圖像二進(jìn)制碼的第一個(gè)字節(jié)就可以得到圖片的格式。一般在開發(fā)中
常用的圖片格式是Jpg暮蹂、PNG寞缝、SVG和GIF。其中
Jpg格式適合于從網(wǎng)絡(luò)上下載的仰泻、像素豐富荆陆、體積略大的圖片(壓縮空間比較大);
png適合于存儲(chǔ)于客戶端的小圖標(biāo)集侯,但是需要為不同屏幕的圖標(biāo)大小做適配(*1被啼、*2、*3)棠枉;
SVG為矢量圖浓体,可以避開屏幕大小適配的問題,也是使用很廣泛的圖片格式辈讶;
GIF為動(dòng)態(tài)圖命浴,其帶有時(shí)間軸。