iOS技巧之圓角圖片的處理

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

處理思路

1.png

RoundCornerManager

定義一個RoundCornerManager的單例,它也是按照上圖的思路去處理圓角圖片,圓角都需要用到圓角圖片的可以通過它去獲得。

@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)存緩存使用的是第三方庫[YYCache](https://github.com/ibireme/YYCache)。這里使用了兩個cache,一個cache是以圖片的URL為KEY去儲存圖片URL的MD5,一個cache是以圖片URL的MD5去儲存圖片本身。同時磁盤也是用圖片URL的MD5值去儲存圓角圖片

###圖片的磁盤緩存
  上面單例初始化中調(diào)用checkAvatarCachePath方法是檢查是否已經(jīng)生成了放置圓角圖片的文件夾,沒有則生成對應(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];///這個是生成指定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是因為不用每次都要計算圖片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(@"通過內(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(@"通過本地緩存獲取圖片");
                        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(@"通過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使用圓角圖片管理單例對外提供獲取圓角圖片的功能。

@implementation UIImageView (JZ)
JZSYNTH_DYNAMIC_PROPERTY_OBJECT(sentinel, setSentinel, RETAIN, JZSentinel *) //使用runtime實現(xiàn)category屬性的宏,自定義的UIImageView的設(shè)置圓角圖片的次數(shù)統(tǒng)計
JZSYNTH_DYNAMIC_PROPERTY_OBJECT(jzImageURL, setJzImageURL, COPY, NSString *)//使用runtime實現(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);
  //下面通過圓角圖片單例獲得圓角圖片
    [[JZAvatarManager shareInstance] getAvatar:url type:AvatarTypeRoundCorner completion:^(UIImage *img) {
        @strongify(self);
        if (!self) {return;}
        if (self.sentinel.value == value) {  //從請求圓角圖片到獲得的過程中這個UIImageView沒有被再次設(shè)置圖片
            self.image = img;
        }
    }];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,296評論 2 376

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

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