iOS 圖片漸進式下載

為了省去制作圖片的麻煩,我就直接拿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);
    
}

效果圖

yscrollsss.gif

可以看到,一個明顯的下拉效果,仔細看確實是有了模糊到清晰的過程??墒沁@個下拉似乎不怎么好看啊,而且這個模糊效果真心不好,好像是太清晰了,這不是我們需要的優(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左右。

progress.gif

Gif制作不是很清晰,實際效果要比這個好很多。
有興趣的同學可以去我GitHub下載這個Demo

既然說了這個圖片下載,還有個動態(tài)圖加載圖片跟大家分享一下。

大致是這樣的

animation.gif

在以前,我們都是放一張靜態(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格式的圖片
mew_progressive.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,094評論 25 709
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,399評論 4 61
  • 2015-12-24 ㈠做的有價值的事情 ①背英語單詞,晨跑 ②VFP課程(第三章) ③看視頻《大眾理財》 ④普通...
    tang小米閱讀 871評論 102 5
  • 高歌猛進的票房證實這是一部成功的商業(yè)片。 首先,作為塑造英雄的類型片,它確實是態(tài)度端正的誠意之作,好萊塢大片中的元...
    夜風如歌閱讀 241評論 0 0
  • 夸父逐日,女媧補天;嫦娥奔月,張生煮海。麻姑說,我看見滄海變桑田;竇娥講,六月大雪為我降人間。《封神演義》里的千里...
    我在萬萬寫字的地方閱讀 369評論 0 0

友情鏈接更多精彩內(nèi)容