iOS中如何判斷一張圖片是否為GIF并貼上標(biāo)簽

在項(xiàng)目中,遇到一個(gè)問(wèn)題,為GIF圖片右下角加上一個(gè)動(dòng)圖的圖標(biāo),于是百度了一下,發(fā)現(xiàn)普遍都是這樣搞的:

NSData *data = [NSData dataWithContentsOfURL: [NSURL URLWithString: imageUrl]];
[data getBytes:&c length:1];
if (c == 0x47) {
    //如果是GIF,即便是長(zhǎng)GIF也會(huì)顯示動(dòng)圖
    self.longImageLabelText.text = @"動(dòng)圖";
}

跑在機(jī)子上一看,的確是能加上圖標(biāo)了,但是要卡幾秒。。這是因?yàn)?code>data的解析需要時(shí)間,阻塞了主線程。

于是,我便開(kāi)多一個(gè)線程咯,反正開(kāi)線程又不會(huì)阻塞主線程..

dispatch_group_async(group, queue, ^{
    //獲取字節(jié),用于判斷動(dòng)圖
    NSData *data = [NSData dataWithContentsOfURL: [NSURL URLWithString: imageUrl]];
    [data getBytes:&c length:1];
    dispatch_async(dispatch_get_main_queue(), ^{
        if (c == 0x47 || isLong) {
            self.longImageLabel.hidden = NO;
            if (isLong) {
                //如果是長(zhǎng)圖
                self.longImageLabelText.text = @"長(zhǎng)圖";
            }
            if (c == 0x47) {
                //如果是GIF,即便是長(zhǎng)GIF也會(huì)顯示動(dòng)圖
                self.longImageLabelText.text = @"動(dòng)圖";
            }
        } else {
            self.longImageLabel.hidden = YES;
        }
    });
});

emmm,這樣的確是不阻塞主線程了,但是有時(shí)候圖動(dòng)起來(lái)了都沒(méi)加上相應(yīng)的標(biāo)簽..

一怒之下看了這個(gè)解析GIF的代碼究竟是何方神圣:

- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock {
    __weak typeof(self)weakSelf = self;
    [self sd_internalSetImageWithURL:url
                    placeholderImage:placeholder
                             options:options
                        operationKey:nil
                       setImageBlock:^(UIImage *image, NSData *imageData) {
                           SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:imageData];
                           if (imageFormat == SDImageFormatGIF) {
                               weakSelf.animatedImage = [FLAnimatedImage animatedImageWithGIFData:imageData];
                               weakSelf.image = nil;
                           } else {
                               weakSelf.image = image;
                               weakSelf.animatedImage = nil;
                           }
                       }
                            progress:progressBlock
                           completed:completedBlock];
}

咦,里面已經(jīng)有關(guān)于GIF的判斷了,那其實(shí)我們直接在他判斷為GIF的時(shí)候加上動(dòng)圖的標(biāo)簽就可以了,但是?。∵@是FLAnimatedImageView的內(nèi)容,那當(dāng)然就不可以直接改啦!這時(shí)候就要用到我們的類(lèi)別了!

- (BOOL)cc_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                 completed:(nullable SDExternalCompletionBlock)completedBlock {
    __block BOOL isGIF = NO;
    __weak typeof(self)weakSelf = self;
    [self sd_internalSetImageWithURL:url
                    placeholderImage:placeholder
                             options:options
                        operationKey:nil
                       setImageBlock:^(UIImage *image, NSData *imageData) {
                           SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:imageData];
                           if (imageFormat == SDImageFormatGIF) {
                               weakSelf.animatedImage = [FLAnimatedImage animatedImageWithGIFData:imageData];
                               weakSelf.image = nil;
                               isGIF = YES;
                           } else {
                               weakSelf.image = image;
                               weakSelf.animatedImage = nil;
                               isGIF = NO;
                           }
                       }
                            progress:progressBlock
                           completed:completedBlock];
    return isGIF;
}

沒(méi)錯(cuò),只要在類(lèi)別中重寫(xiě)這個(gè)函數(shù),然后再加上isGIF的返回類(lèi)型,調(diào)用這個(gè)類(lèi)別的方法,即可獲取是否為GIF了!


更新于8.2

以上方法雖然可以做到判斷GIF,但是還是會(huì)有偶發(fā)現(xiàn)象:GIF標(biāo)簽有時(shí)候并不能及時(shí)地貼上

在debug過(guò)程中可以發(fā)現(xiàn),在加載GIF時(shí),imageFormat并不會(huì)立即被判斷為SDImageFormatGIF,而是SDImageFormatUndefined,然后再看看源碼,發(fā)現(xiàn)是因?yàn)?code>imageData一開(kāi)始被設(shè)置為nil,導(dǎo)致圖片類(lèi)型被判斷為SDImageFormatUndefined了。

為什么Data會(huì)被設(shè)置為nil?

科普一下options參數(shù)的作用,來(lái)源在這里

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {

    SDWebImageRetryFailed = 1 << 0,

    SDWebImageLowPriority = 1 << 1,

    SDWebImageCacheMemoryOnly = 1 << 2,

    SDWebImageProgressiveDownload = 1 << 3,

    SDWebImageRefreshCached = 1 << 4,

    SDWebImageContinueInBackground = 1 << 5,

    SDWebImageHandleCookies = 1 << 6,

    SDWebImageAllowInvalidSSLCertificates = 1 << 7,

    SDWebImageHighPriority = 1 << 8,

    SDWebImageDelayPlaceholder = 1 << 9,

    SDWebImageTransformAnimatedImage = 1 << 10,

    SDWebImageAvoidAutoSetImage = 1 << 11
};

SDWebImageRetryFailed = 1 << 0,:默認(rèn)情況下,如果一個(gè)url在下載的時(shí)候失敗了,那么這個(gè)url會(huì)被加入黑名單并且library不會(huì)嘗試再次下載,這個(gè)flag會(huì)阻止library把失敗的url加入黑名單(簡(jiǎn)單來(lái)說(shuō)如果選擇了這個(gè)flag,那么即使某個(gè)url下載失敗了,sdwebimage還是會(huì)嘗試再次下載他

SDWebImageLowPriority = 1 << 1,:默認(rèn)情況下,圖片會(huì)在交互發(fā)生的時(shí)候下載(例如你滑動(dòng)tableview的時(shí)候),這個(gè)flag會(huì)禁止這個(gè)特性,導(dǎo)致的結(jié)果就是在scrollview減速的時(shí)候,才會(huì)開(kāi)始下載(也就是你滑動(dòng)的時(shí)候scrollview不下載,你手從屏幕上移走,scrollview開(kāi)始減速的時(shí)候才會(huì)開(kāi)始下載圖片

SDWebImageCacheMemoryOnly = 1 << 2,:這個(gè)flag禁止磁盤(pán)緩存,只有內(nèi)存緩存

SDWebImageProgressiveDownload = 1 << 3,:這個(gè)flag會(huì)在圖片下載的時(shí)候就顯示(就像你用瀏覽器瀏覽網(wǎng)頁(yè)的時(shí)候那種圖片下載,一截一截的顯示(待確認(rèn)))

SDWebImageRefreshCached = 1 << 4,:一個(gè)圖片緩存了,還是會(huì)重新請(qǐng)求.并且緩存?zhèn)嚷砸罁?jù)NSURLCache而不是SDWebImage.URL不變,圖片會(huì)更新時(shí)使用

SDWebImageContinueInBackground = 1 << 5,:啟動(dòng)后臺(tái)下載,加入你進(jìn)入一個(gè)頁(yè)面,有一張圖片正在下載這時(shí)候你讓app進(jìn)入后臺(tái),圖片還是會(huì)繼續(xù)下載(這個(gè)估計(jì)要開(kāi)backgroundfetch才有用)

SDWebImageHandleCookies = 1 << 6,:可以控制存在NSHTTPCookieStore的cookies.

SDWebImageAllowInvalidSSLCertificates = 1 << 7,:允許不安全的SSL證書(shū),在正式環(huán)境中慎用

SDWebImageHighPriority = 1 << 8,:默認(rèn)情況下,image在裝載的時(shí)候是按照他們?cè)陉?duì)列中的順序裝載的(就是先進(jìn)先出).這個(gè)flag會(huì)把他們移動(dòng)到隊(duì)列的前端,并且立刻裝載,而不是等到當(dāng)前隊(duì)列裝載的時(shí)候再裝載.

SDWebImageDelayPlaceholder = 1 << 9,:默認(rèn)情況下,占位圖會(huì)在圖片下載的時(shí)候顯示.這個(gè)flag開(kāi)啟會(huì)延遲占位圖顯示的時(shí)間,等到圖片下載完成之后才會(huì)顯示占位圖.

SDWebImageTransformAnimatedImage = 1 << 10,:是否transform圖片

而源碼中有這樣的語(yǔ)句:

if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
        });
    }

可以看到,在顯示占位圖模式(不設(shè)置SDWebImageDelayPlaceholder)中,首先這個(gè)方法會(huì)先加載一張占位圖,與此同時(shí),原圖(GIF等)中的data會(huì)先被設(shè)置為nil,導(dǎo)致這張圖片先會(huì)被設(shè)置為SDImageFormatUndefined。

而在之前的方法中,如果不是GIF類(lèi)型則直接返回NO,也導(dǎo)致了標(biāo)簽會(huì)直接不顯示出來(lái),等到真正加載GIF的時(shí)候,標(biāo)簽已成定局,也就是說(shuō),無(wú)法再為動(dòng)圖加上標(biāo)簽了。

所以,之前提到的直接返回一個(gè)BOOL變量的方法是不太準(zhǔn)確的,最好的方法是使用委托模式或者觀察者模式,委托或者通知。

但是這是一個(gè)類(lèi)別啊喂,怎么擁有delegate變量?委托這條路顯然是走不通的了。那就用通知吧。

我們知道,通知是那種一對(duì)多的關(guān)系,怎么讓通知變成一對(duì)一呢?先來(lái)學(xué)習(xí)一下通知的幾個(gè)變量:

iOS 通知(NSNotification)的簡(jiǎn)單使用中提到,NSNotification中,各種參數(shù)分別是:

name:是消息對(duì)象的唯一標(biāo)識(shí),接受通知消息時(shí)用來(lái)辨別

@property (readonly,copy)NSNotificationName name;

object:一個(gè)對(duì)象,可以理解為針對(duì)某個(gè)對(duì)象的消息

@property (nullable,readonly,retain)id object;

userInfo:一個(gè)字典,用來(lái)傳值

@property (nullable,readonly,copy)NSDictionary *userInfo;

其中,nameobject不設(shè)為nil的話就是一對(duì)一關(guān)系了。圖片的URL對(duì)應(yīng)的String即為唯一標(biāo)識(shí)符。

喲西,思路已經(jīng)很清晰了,當(dāng)我們加載到一張GIF圖片的時(shí)候,我們就會(huì)發(fā)出一個(gè)通知,讓cell去顯示這個(gè)動(dòng)圖的標(biāo)簽,否則,則要么隱藏標(biāo)簽,要么顯示長(zhǎng)圖的標(biāo)簽。

SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:imageData];
if (imageFormat == SDImageFormatGIF) {
   weakSelf.animatedImage = [FLAnimatedImage animatedImageWithGIFData:imageData];
   weakSelf.image = nil;
   NSNotification * notice = [NSNotification notificationWithName: imageUrl object: imageUrl userInfo: @{@"isGIF": @"GIF"}];
   [[NSNotificationCenter defaultCenter]postNotification:notice];
} else {
   weakSelf.image = image;
   weakSelf.animatedImage = nil;
   NSNotification * notice = [NSNotification notificationWithName: imageUrl object: imageUrl userInfo: @{@"isGIF": @"NotGIF"}];
   [[NSNotificationCenter defaultCenter]postNotification:notice];
}

同時(shí),再為cell注冊(cè)通知和響應(yīng)方法

NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self selector: @selector(setGIFLabel:) name: imageUrl object: imageUrl];
- (void)setGIFLabel: (NSNotification *)notification {
    BOOL isGIF = [notification.userInfo[@"isGIF"] isEqualToString: @"GIF"];
    if (isGIF || self.isLong) {
        self.longImageLabel.hidden = NO;
        if (isGIF)
            self.longImageLabelText.text = @"動(dòng)圖";
        else
            self.longImageLabelText.text = @"長(zhǎng)圖";
    } else {
        self.longImageLabel.hidden = YES;
    }
}

這樣就可以很及時(shí)準(zhǔn)確地將動(dòng)圖貼上自己的標(biāo)簽啦!


更新于8.3

因?yàn)樾阅軉?wèn)題被QA指出來(lái)批評(píng)了..雖然通知不是很耗性能吧,但是一對(duì)一的通知,總感覺(jué)怪怪的..還是改成委托比較好。

之前我說(shuō)過(guò),類(lèi)別不能用委托,所以這次我們改成繼承吧。繼承于基類(lèi)ImageView,然后在Cell中改成自定義imageView即可。

最后聲明協(xié)議和方法,在對(duì)應(yīng)的情形下使用對(duì)應(yīng)的協(xié)議即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,326評(píng)論 25 708
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,284評(píng)論 8 265
  • 為著下月的水彩教學(xué),特地去挖出壓在房角的畫(huà)具箱裡找水彩筆;竟不意看到我收藏在內(nèi)沉睡快30年的老舊作, 那是些我當(dāng)年...
    香羅玲璫閱讀 375評(píng)論 2 1
  • 2016,我22歲,處于大四的迷茫探索時(shí)期,一個(gè)掙扎求存的轉(zhuǎn)折點(diǎn),一次義無(wú)返顧的旅行。 上篇-西電 2016年考研...
    analanxingde閱讀 293評(píng)論 0 0
  • 原來(lái)你笑起來(lái)這么好看妖怪說(shuō)我在想我的愛(ài)人我說(shuō)玉米在風(fēng)里嘩嘩作響愛(ài)情是什么感覺(jué)?妖怪趴在玉米地里一臉迷惑愛(ài)情的感覺(jué)就...
    葺寶閱讀 181評(píng)論 0 1

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