為了省去制作圖片的麻煩,我就直接拿YYWebImage里面的圖片了,我個人也是建議使用這個圖片框架來做漸進式下載。
先看下YYKit中做的效果圖。
圖片加載很美觀,用戶體驗性非常棒。當我第一次看到的時候,就興奮的直接拿著代碼去用,但是發(fā)現(xiàn)并不行,沒有效果。后來查了資料才知道這種下載是有要求的。大家若測試,可以用我下面這個代碼URL。
NSURL *url = [NSURL URLWithString:@"http://og3u5glro.bkt.clouddn.com/%E6%B8%90%E8%BF%9B%E5%BC%8F%E5%9B%BE%E7%89%87.jpg"];
[self.iconImageView yy_setImageWithURL:url
options:YYWebImageOptionProgressiveBlur];
但是,僅僅吃人家給的魚干,遠遠滿足不了我等對學習的渴望。于是,在和一個群友討論后,決定自己寫出這樣的一個效果。
下面我先簡單解釋下圖片格式
然后我會貼上自己實現(xiàn)這個漸進式下載的代碼思路
最后當然是對你們來說最關(guān)心的,在文末我將提供一個簡單的Demo。
言歸正傳,先來解釋下,為什么你從百度隨便弄一張圖的鏈接放上去,但沒有漸進式的下載效果。
實際上這和圖片的格式支持有關(guān)。
一般來說我們碰到的圖片大多都是Baseline JPEG這種格式的圖片,我們也稱為標準格式。然而漸進式圖片下載,則需要Progressive JPEG這個格式,稱之為漸進式,當然還有Interlaced格式等等,說到這里也許你已經(jīng)明白了,是圖片格式本身就不支持,不是代碼沒作用!下面說下這兩個格式。
Baseline JPEG
標準格式的圖片,是從上往下的方式逐行進行掃描。也就是看起來是一行一行的下載繪制,細心的同學會發(fā)現(xiàn),YYWebImage里面就有這樣的下載設(shè)置,代碼如下:
[imageView yy_setImageWithURL:url options:YYWebImageOptionProgressive];
那看起來大概是這樣的

Progressive JPEG
漸進式格式圖片的掃描是多次的,再打開圖片的過程中,先顯示他的輪廓,在慢慢掃描繪制所有的色塊,最終清晰。效果最上面大家已經(jīng)看過了,這種技術(shù)被廣泛應(yīng)用于大圖的下載顯示上。
漸進式圖片的一些小缺點:最初繪制的模糊圖片,實際上與原圖的大小有相差、這種繪制更加消耗CPU...
那么,這種圖片如何制作呢?
很簡單,在photoshop中有存儲為web所用格式,打開后選擇連續(xù)就是Progressive JPEG。
這樣美觀的漸進式下載,我如何實現(xiàn)呢?
圖片解碼需要用到這個框架處理
#import <ImageIO/ImageIO.h>
首先使用CGImageSourceCreateIncremental(NULL)創(chuàng)建圖片源,然后在網(wǎng)絡(luò)請求代理中拼接每次返回的圖片data,使用CGImageSourceUpdateData更新圖片數(shù)據(jù),最后使用CGImageSourceCreateImageAtIndex來創(chuàng)建圖片顯示
其實過程就這么多,ok,貼上主要代碼,運行看看效果。
@interface ViewController ()<NSURLSessionDelegate> {
NSMutableData * _recieveData;//當前下載date
long long _expectedLeght;//預(yù)估大小
CGImageSourceRef _incrementallyImgSource;
}
@property(nonatomic, strong) UIImageView *imageView;
/**
漸進式加載圖片
*/
- (void)loadImage {
_incrementallyImgSource = CGImageSourceCreateIncremental(NULL);
_recieveData = [[NSMutableData alloc] init];
[self.view addSubview:self.imageView];
NSURL *url = [NSURL URLWithString:@"http://og3u5glro.bkt.clouddn.com/%E6%B8%90%E8%BF%9B%E5%BC%8F%E5%9B%BE%E7%89%87.jpg"];
NSURLSession *session=[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLRequest *request=[NSURLRequest requestWithURL:url];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
[task resume];
}
// 1.接收到服務(wù)器的響應(yīng)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
// 允許處理服務(wù)器的響應(yīng),才會繼續(xù)接收服務(wù)器返回的數(shù)據(jù)
completionHandler(NSURLSessionResponseAllow);
_expectedLeght = response.expectedContentLength;
NSLog(@"_expectedLeght %lld",_expectedLeght);
}
// 2.接收到服務(wù)器的數(shù)據(jù)(可能調(diào)用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
[data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
[_recieveData appendBytes:bytes length:byteRange.length];
}];
BOOL isloadFinish = NO;
if (_expectedLeght == _recieveData.length) {
isloadFinish = YES;
}
CGImageSourceUpdateData(_incrementallyImgSource, (CFDataRef)_recieveData, isloadFinish);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(_incrementallyImgSource, 0, NULL);
UIImage *imageaa = [UIImage imageWithCGImage:imageRef];
self.imageView.image = imageaa;
CGImageRelease(imageRef);
NSLog(@"_recieveData %lu",(unsigned long)_recieveData.length);
}
效果圖

可以看到,一個明顯的下拉效果,仔細看確實是有了模糊到清晰的過程??墒沁@個下拉似乎不怎么好看啊,而且這個模糊效果真心不好,好像是太清晰了,這不是我們需要的優(yōu)美的體驗。
那么,模糊效果不好怎么處理呢?我第一時間想到的就是,我要再加一層毛玻璃!以此獲得更好的用戶體驗。
毛玻璃處理應(yīng)該是個動態(tài)的,請看下面代碼
//毛玻璃處理
- (UIImage *)filterWith:(UIImage *)image andRadius:(CGFloat)radius {
CIImage *inputImage = [[CIImage alloc] initWithCGImage:image.CGImage];
CIFilter *affineClampFilter = [CIFilter filterWithName:@"CIAffineClamp"];
CGAffineTransform xform = CGAffineTransformMakeScale(1.0, 1.0);
[affineClampFilter setValue:inputImage forKey:kCIInputImageKey];
[affineClampFilter setValue:[NSValue valueWithBytes:&xform
objCType:@encode(CGAffineTransform)]
forKey:@"inputTransform"];
CIImage *extendedImage = [affineClampFilter valueForKey:kCIOutputImageKey];
CIFilter *blurFilter =
[CIFilter filterWithName:@"CIGaussianBlur"];
[blurFilter setValue:extendedImage forKey:kCIInputImageKey];
[blurFilter setValue:@(radius) forKey:@"inputRadius"];
CIImage *result = [blurFilter valueForKey:kCIOutputImageKey];
CIContext *ciContext = [CIContext contextWithOptions:nil];
CGImageRef cgImage = [ciContext createCGImage:result fromRect:inputImage.extent];
UIImage *uiImage = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
return uiImage;
}
方法寫好,那么調(diào)用看看吧!
// 2.接收到服務(wù)器的數(shù)據(jù)(可能調(diào)用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
[data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
[_recieveData appendBytes:bytes length:byteRange.length];
}];
BOOL isloadFinish = NO;
if (_expectedLeght == _recieveData.length) {
isloadFinish = YES;
}
//不希望出現(xiàn)下拉效果 同時避免數(shù)據(jù)太少毛玻璃繪制crash
// if (_recieveData.length <= _expectedLeght*0.12) {
// return;
// }
CGImageSourceUpdateData(_incrementallyImgSource, (CFDataRef)_recieveData, isloadFinish);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(_incrementallyImgSource, 0, NULL);
UIImage *imageaa = [UIImage imageWithCGImage:imageRef];
CGFloat length = _expectedLeght;
CGFloat dataLength = _recieveData.length;
NSLog(@"....%f",dataLength/length);
self.imageView.image = [self filterWith:imageaa andRadius:(1-dataLength/length)*10];
CGImageRelease(imageRef);
NSLog(@"_recieveData %lu",(unsigned long)_recieveData.length);
}
效果圖,迫不及待~
為了看清下載效果,找的圖片很大2.8M左右。

Gif制作不是很清晰,實際效果要比這個好很多。
有興趣的同學可以去我GitHub下載這個Demo
既然說了這個圖片下載,還有個動態(tài)圖加載圖片跟大家分享一下。
大致是這樣的

在以前,我們都是放一張靜態(tài)圖的,當圖片下載完成,更改圖片,也就是著名的 PlaceholderImage 。如果我想放一張動態(tài)的placeholderImage怎么辦?我推薦使用YYImage,實現(xiàn)步驟如下:
第一步:把你的imageView繼承 YYAnimatedImageView(似乎不繼承也可以,我記不清了)
第二步:
[self.iconImageView sd_setImageWithURL:[NSURL URLWithString:url]
placeholderImage:[YYImage imageNamed:@"placeImage.gif"]];
有一點不得不說:萬惡的卡頓!雖然已經(jīng)優(yōu)化的不錯了,但是仍然出現(xiàn)了偶爾輕微卡頓的情況,可能我有強迫癥??傊@樣不能眾享絲滑了。如果有老司機有辦法一路滑下去,請帶帶我!
iOS技術(shù)交流群:511860085 歡迎加入!
最后放一張Progressive JPEG格式的圖片
