iOS技巧之圓角圖片的處理

目前很多應(yīng)用很多地方如頭像、背景等都喜歡大量使用帶圓角的圖片,若是簡(jiǎn)單粗暴的使用設(shè)置cornerRadius和maskTOBounds方法會(huì)引起大家都知道的離屏渲染從而帶來(lái)嚴(yán)重的性能問(wèn)題導(dǎo)致用戶滑動(dòng)界面會(huì)感受到明顯的卡頓。
一般來(lái)說(shuō)我們可以通過(guò)在后臺(tái)把方形圖片進(jìn)行重繪成帶圓角的继准,然后再主線程中去更新茅姜。這篇文章的重點(diǎn)在于繪制完這些圓角圖片后該怎么處理它們的問(wèn)題,關(guān)鍵詞是 “緩存與持久化”怕篷。

處理思路

1.png

RoundCornerManager

定義一個(gè)RoundCornerManager的單例,它也是按照上圖的思路去處理圓角圖片酗昼,圓角都需要用到圓角圖片的可以通過(guò)它去獲得廊谓。

@interface JZAvatarManager()
@property (nonatomic, copy) NSString *roundCornerFolderPath;
@property (nonatomic, strong) dispatch_queue_t conQueue;
@property (nonatomic, strong) YYMemoryCache *memoryCache;
@property (nonatomic, strong) YYMemoryCache *md5Cache;
@end

@implementation JZAvatarManager
static JZAvatarManager *_instance;
+ (instancetype)shareInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [JZAvatarManager new];
        
    });
    return _instance;
}

- (instancetype)init {
    self = [super init];
    self.conQueue = dispatch_queue_create("cn.n8n8.circle.Avatar", DISPATCH_QUEUE_CONCURRENT);
    self.memoryCache = [YYMemoryCache new];
    self.memoryCache.shouldRemoveAllObjectsOnMemoryWarning = true;
    self.memoryCache.shouldRemoveAllObjectsWhenEnteringBackground = false;
    self.memoryCache.releaseOnMainThread = true;
    self.memoryCache.name = @"avatarCache";
    self.md5Cache = [YYMemoryCache new];
    self.md5Cache.shouldRemoveAllObjectsOnMemoryWarning = false;
    self.md5Cache.shouldRemoveAllObjectsWhenEnteringBackground = false;
    self.md5Cache.name = @"md5Cache";
    [self checkAvatarCachePath];   
    return self;
}```

###圖片的內(nèi)存緩存
  上面單例初始化中的內(nèi)存緩存使用的是第三方庫(kù)[YYCache](https://github.com/ibireme/YYCache)。這里使用了兩個(gè)cache麻削,一個(gè)cache是以圖片的URL為KEY去儲(chǔ)存圖片URL的MD5蹂析,一個(gè)cache是以圖片URL的MD5去儲(chǔ)存圖片本身。同時(shí)磁盤也是用圖片URL的MD5值去儲(chǔ)存圓角圖片

###圖片的磁盤緩存
  上面單例初始化中調(diào)用checkAvatarCachePath方法是檢查是否已經(jīng)生成了放置圓角圖片的文件夾碟婆,沒有則生成對(duì)應(yīng)的文件夾电抚。
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSString *pathDocuments = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *folderPath = [pathDocuments stringByAppendingPathComponent:@"avatar"];
self.roundCornerFolderPath = folderPath;
// 判斷文件夾是否存在,如果不存在竖共,則創(chuàng)建
if (![[NSFileManager defaultManager] fileExistsAtPath:folderPath]) {
    JZLog(@"創(chuàng)建文件夾成功 %@", folderPath);
    [fileManager createDirectoryAtPath:folderPath withIntermediateDirectories:YES attributes:nil error:nil];
    [fileManager createDirectoryAtPath:folderPath withIntermediateDirectories:YES attributes:nil error:nil];
}```

單例的處理流程

-(void)getAvatar:(NSString *)url type:(AvatarType)type completion:(void (^)(UIImage *))completion {
    static dispatch_once_t onceToken;
    static NSString *avatarSizeStr;
    dispatch_once(&onceToken, ^{
        CGFloat scale =[[UIScreen mainScreen] scale];
        NSInteger width = 50 * scale;
        NSInteger height = 50 * scale;
        avatarSizeStr = [NSString stringWithFormat:@"?imageView2/1/w/%ld/h/%ld",width,height];
    });
    NSString *newURL = [NSString stringWithFormat:@"%@%@",url,avatarSizeStr];///這個(gè)是生成指定size的圖片URL
    NSURL *imgURL = [NSURL URLWithString:newURL];
    NSString *componentMD5;
    if ([self.md5Cache containsObjectForKey:newURL]) {
        componentMD5 = [self.md5Cache objectForKey:newURL];
    } else {
        componentMD5 = [newURL jz_md5String];
        [self.md5Cache setObject:componentMD5 forKey:newURL]; //圖片URL的MD5放入cache中蝙叛。
    }
//獲得根據(jù)圖片URL生成的MD5,采用cache是因?yàn)椴挥妹看味家?jì)算圖片URL的MD5
    if (type == AvatarTypeOrigin) {
        [[SDWebImageManager sharedManager] downloadImageWithURL:imgURL options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            if (image) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    completion(image);
                });
            }
        }];
    } else {
        NSString *completePath = [self.roundCornerFolderPath stringByAppendingPathComponent:componentMD5];
        if ([self.memoryCache containsObjectForKey:componentMD5]) { //查詢cache是有已經(jīng)有圓角圖片
            UIImage *img = [self.memoryCache objectForKey:componentMD5];
            dispatch_async(dispatch_get_main_queue(), ^{
                //JZLog(@"通過(guò)內(nèi)存緩存獲取圖片");
                completion(img);
            });
        } else {
            if ([[NSFileManager defaultManager] fileExistsAtPath:completePath]) { //查詢本地是否有圓角圖片
                NSData *data = [NSData dataWithContentsOfFile:completePath];
                UIImage *img = [UIImage imageWithData:data];
                if (completion) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        //JZLog(@"通過(guò)本地緩存獲取圖片");
                        completion(img);
                        [self.memoryCache setObject:img forKey:componentMD5];
                    });
                }
            } else {
                [[SDWebImageManager sharedManager] downloadImageWithURL:imgURL options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                    if (image && finished) {
                        dispatch_async(self.conQueue, ^{
                            UIImage *img = [self AddRoundCornerToImage:image];
                            if (completion) {
                                dispatch_async(dispatch_get_main_queue(), ^{
                                    //JZLog(@"通過(guò)sd下載");
                                    completion(img);
                                });
                            }
                            [UIImagePNGRepresentation(img) writeToFile:completePath atomically:true];  //圓角圖片寫入磁盤
                            [self.memoryCache setObject:img forKey:componentMD5];//圓角圖片寫入內(nèi)存
                        });
                    }
                }];
            }
        }
    }
}

//繪制圓角圖片
- (UIImage *)AddRoundCornerToImage: (UIImage *)source {
    CGFloat w = source.size.width;
    CGFloat h = source.size.height;
    CGFloat scale = [UIScreen mainScreen].scale;
    CGFloat cornerRadius = MIN(w, h) / 2.;
    
    CGRect newImgRect = CGRectMake((w - MIN(w, h))/2, 0, MIN(w, h), MIN(w, h));
    CGImageRef newCGImg = CGImageCreateWithImageInRect(source.CGImage, newImgRect);
    UIImage *newImg = [UIImage imageWithCGImage:newCGImg];
    CGImageRelease(newCGImg);
    
    UIImage *roundImage = nil;
    CGRect imageFrame = CGRectMake(0.0, 0.0, newImg.size.width, newImg.size.height);
    UIGraphicsBeginImageContextWithOptions(newImg.size, NO, scale);
    [[UIBezierPath bezierPathWithRoundedRect:imageFrame cornerRadius:cornerRadius] addClip];
    [newImg drawInRect:imageFrame];
    roundImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return roundImage;
}

提供便利的Category獲取圓角圖片

我們可以利用UIImageView或UIButton的Category使用圓角圖片管理單例對(duì)外提供獲取圓角圖片的功能公给。

@implementation UIImageView (JZ)
JZSYNTH_DYNAMIC_PROPERTY_OBJECT(sentinel, setSentinel, RETAIN, JZSentinel *) //使用runtime實(shí)現(xiàn)category屬性的宏借帘,自定義的UIImageView的設(shè)置圓角圖片的次數(shù)統(tǒng)計(jì)
JZSYNTH_DYNAMIC_PROPERTY_OBJECT(jzImageURL, setJzImageURL, COPY, NSString *)//使用runtime實(shí)現(xiàn)category屬性的宏蜘渣,自定義的UIImageView的原地址的屬性



-(void)jz_setRoundCornerAvatarImageWithURL:(NSString *)url placeHolderImage:(UIImage *)placeHolder {
  
    dispatch_async(dispatch_get_main_queue(), ^{
        if (placeHolder) {
            self.image = placeHolder;
        } else {
            self.image = [UIImage imageNamed:@"placeHolder_avatar"];
        }
    });
    if (!self.sentinel) {
        self.sentinel = [JZSentinel new];
    }
    int32_t value = [self.sentinel increase];  //UIImageView設(shè)置圖片的次數(shù)加1
    @weakify(self);
  //下面通過(guò)圓角圖片單例獲得圓角圖片
    [[JZAvatarManager shareInstance] getAvatar:url type:AvatarTypeRoundCorner completion:^(UIImage *img) {
        @strongify(self);
        if (!self) {return;}
        if (self.sentinel.value == value) {  //從請(qǐng)求圓角圖片到獲得的過(guò)程中這個(gè)UIImageView沒有被再次設(shè)置圖片
            self.image = img;
        }
    }];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肺然,隨后出現(xiàn)的幾起案子蔫缸,更是在濱河造成了極大的恐慌,老刑警劉巖际起,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拾碌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡街望,警方通過(guò)查閱死者的電腦和手機(jī)校翔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)灾前,“玉大人防症,你說(shuō)我怎么就攤上這事“ゼ祝” “怎么了蔫敲?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)炭玫。 經(jīng)常有香客問(wèn)我奈嘿,道長(zhǎng),這世上最難降的妖魔是什么础嫡? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任指么,我火速辦了婚禮酝惧,結(jié)果婚禮上榴鼎,老公的妹妹穿的比我還像新娘。我一直安慰自己晚唇,他們只是感情好巫财,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哩陕,像睡著了一般平项。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悍及,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天闽瓢,我揣著相機(jī)與錄音,去河邊找鬼心赶。 笑死扣讼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缨叫。 我是一名探鬼主播椭符,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼荔燎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了销钝?” 一聲冷哼從身側(cè)響起有咨,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蒸健,沒想到半個(gè)月后座享,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纵装,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年征讲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片橡娄。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诗箍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挽唉,到底是詐尸還是另有隱情滤祖,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布瓶籽,位于F島的核電站匠童,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏塑顺。R本人自食惡果不足惜汤求,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望严拒。 院中可真熱鬧扬绪,春花似錦、人聲如沸裤唠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)种蘸。三九已至墓赴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間航瞭,已是汗流浹背诫硕。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留刊侯,地道東北人章办。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親纲菌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挠日,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件翰舌、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,124評(píng)論 4 61
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,168評(píng)論 30 470
  • 如果有一天嚣潜,我從你的世界消失了,你會(huì)不會(huì)在街上走著走著突然想到我椅贱,站著愣神好久懂算; 如果有一天,我從你的世界消失了庇麦,...
    阿月渾子i閱讀 277評(píng)論 0 1
  • 摘自別人的文章计技,先收集素材再慢慢整理總結(jié)吧 一、PM的能力模型=核心能力+通用能力+技能 “產(chǎn)品經(jīng)理”山橄,并非是懂U...
    知遇閱讀 458評(píng)論 0 0
  • 語(yǔ)言是人類最重要的交際工具航棱,是人們進(jìn)行溝通交流的各種表達(dá)方式睡雇。人們借助語(yǔ)言保存和傳遞人類文明的成果。語(yǔ)言是民族的重...
    馮元民閱讀 329評(píng)論 0 0