You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
339 lines
13 KiB
339 lines
13 KiB
// |
|
// KJLoadImageView.m |
|
// iSchool |
|
// |
|
// Created by 杨科军 on 2018/12/22. |
|
// Copyright © 2018 杨科军. All rights reserved. |
|
// |
|
|
|
#import "KJLoadImageView.h" |
|
#import <objc/runtime.h> |
|
#import <CommonCrypto/CommonDigest.h> |
|
|
|
/* 网络下载相关 |
|
* 图片下载器,没有直接使用NSURLSession之类的,因为希望这套库可以支持iOS6 |
|
*/ |
|
@interface KJImageDownloader : NSObject<NSURLSessionDownloadDelegate> |
|
|
|
@property (nonatomic, strong) NSURLSession *session; |
|
@property (nonatomic, strong) NSURLSessionDownloadTask *task; |
|
@property (nonatomic, assign) unsigned long long totalLength; |
|
@property (nonatomic, assign) unsigned long long currentLength; |
|
@property (nonatomic, copy) KJDownloadProgressBlock progressBlock; |
|
@property (nonatomic, copy) KJDownLoadDataCallBack callbackOnFinished; |
|
|
|
/// 下载图片 |
|
- (void)kj_startDownloadImageWithUrl:(NSString*)url Progress:(KJDownloadProgressBlock)progress Complete:(KJDownLoadDataCallBack)complete; |
|
|
|
@end |
|
|
|
@implementation KJImageDownloader |
|
|
|
- (void)kj_startDownloadImageWithUrl:(NSString*)url Progress:(KJDownloadProgressBlock)progress Complete:(KJDownLoadDataCallBack)complete{ |
|
self.progressBlock = progress; |
|
self.callbackOnFinished = complete; |
|
if ([NSURL URLWithString:url] == nil) { |
|
if (complete) { |
|
NSError *error = [NSError errorWithDomain:@"Domain" code:101 userInfo:@{@"message":@"URL不正确"}]; |
|
complete(nil, error); |
|
} |
|
return; |
|
} |
|
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60]; |
|
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; |
|
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; |
|
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; |
|
self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:queue]; |
|
NSURLSessionDownloadTask *task = [self.session downloadTaskWithRequest:request]; |
|
[task resume]; |
|
self.task = task; |
|
} |
|
|
|
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { |
|
NSData *data = [NSData dataWithContentsOfURL:location]; |
|
if (self.progressBlock) { |
|
self.progressBlock(self.totalLength, self.currentLength); |
|
} |
|
if (self.callbackOnFinished) { |
|
self.callbackOnFinished(data, nil); |
|
self.callbackOnFinished = nil;// 防止重复调用 |
|
} |
|
} |
|
|
|
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { |
|
self.currentLength = totalBytesWritten; |
|
self.totalLength = totalBytesExpectedToWrite; |
|
if (self.progressBlock) { |
|
self.progressBlock(self.totalLength, self.currentLength); |
|
} |
|
} |
|
|
|
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { |
|
if ([error code] != NSURLErrorCancelled) { |
|
if (self.callbackOnFinished) { |
|
self.callbackOnFinished(nil, error); |
|
} |
|
self.callbackOnFinished = nil; |
|
} |
|
} |
|
|
|
@end |
|
|
|
/// 缓存相关 |
|
@interface UIApplication (KJCacheImage) |
|
|
|
@property (nonatomic,strong,readonly) NSMutableDictionary *kj_cacheFaileDictionary; |
|
|
|
- (UIImage *)kj_cacheImageForRequest:(NSURLRequest*)request; |
|
- (void)kj_cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request; |
|
- (void)kj_cacheFailRequest:(NSURLRequest *)request; |
|
/// 获取失败次数 |
|
- (NSUInteger)kj_failTimesForRequest:(NSURLRequest *)request; |
|
|
|
@end |
|
|
|
@implementation UIApplication (KJCacheImage) |
|
|
|
- (NSMutableDictionary *)kj_cacheFaileDictionary { |
|
NSMutableDictionary *dict = objc_getAssociatedObject(self, @selector(kj_cacheFaileDictionary)); |
|
if (!dict) { |
|
dict = [[NSMutableDictionary alloc] init]; |
|
objc_setAssociatedObject(self, @selector(kj_cacheFaileDictionary), dict, OBJC_ASSOCIATION_RETAIN_NONATOMIC); |
|
} |
|
return dict; |
|
} |
|
|
|
- (void)kj_clearCache { |
|
[self.kj_cacheFaileDictionary removeAllObjects]; |
|
objc_setAssociatedObject(self, @selector(kj_cacheFaileDictionary), nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); |
|
} |
|
|
|
- (void)kj_clearDiskCaches { |
|
NSString *directoryPath = KJBannerLoadImages; |
|
if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) { |
|
NSError *error = nil; |
|
[[NSFileManager defaultManager] removeItemAtPath:directoryPath error:&error]; |
|
} |
|
[self kj_clearCache]; |
|
} |
|
|
|
- (NSUInteger)kj_failTimesForRequest:(NSURLRequest *)request { |
|
NSNumber *faileTimes = [self.kj_cacheFaileDictionary objectForKey:[self kj_md5:[self kj_keyForRequest:request]]]; |
|
if (faileTimes && [faileTimes respondsToSelector:@selector(integerValue)]) { |
|
return faileTimes.integerValue; |
|
} |
|
return 0; |
|
} |
|
|
|
/// 缓存图片 |
|
- (UIImage *)kj_cacheImageForRequest:(NSURLRequest *)request { |
|
if (request) { |
|
NSString *directoryPath = KJBannerLoadImages; |
|
NSString *path = [NSString stringWithFormat:@"%@/%@", directoryPath, [self kj_md5:[self kj_keyForRequest:request]]]; |
|
return [UIImage imageWithContentsOfFile:path]; |
|
} |
|
return nil; |
|
} |
|
/// |
|
- (void)kj_cacheFailRequest:(NSURLRequest *)request { |
|
NSString *key = [self kj_md5:[self kj_keyForRequest:request]]; |
|
NSNumber *faileTimes = [self.kj_cacheFaileDictionary objectForKey:key]; |
|
NSUInteger times = 0; |
|
if (faileTimes && [faileTimes respondsToSelector:@selector(integerValue)]) { |
|
times = [faileTimes integerValue]; |
|
} |
|
times++; |
|
[self.kj_cacheFaileDictionary setObject:@(times) forKey:key]; |
|
} |
|
|
|
- (void)kj_cacheImage:(UIImage *)image forRequest:(NSURLRequest *)request { |
|
if (image == nil || request == nil) { |
|
return; |
|
} |
|
NSString *directoryPath = KJBannerLoadImages; |
|
if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) { |
|
NSError *error = nil; |
|
[[NSFileManager defaultManager] createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error]; |
|
if (error) return; |
|
} |
|
NSString *path = [NSString stringWithFormat:@"%@/%@",directoryPath,[self kj_md5:[self kj_keyForRequest:request]]]; |
|
NSData *data = UIImagePNGRepresentation(image); |
|
if (data) { |
|
[[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];/// 缓存数据 |
|
} |
|
} |
|
|
|
#pragma mark - 内部方法 |
|
/// 拼接路径 |
|
- (NSString *)kj_keyForRequest:(NSURLRequest *)request { |
|
BOOL portait = NO; |
|
if (UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation])) { |
|
portait = YES; |
|
} |
|
return [NSString stringWithFormat:@"%@%@", request.URL.absoluteString, portait ? @"portait" : @"lanscape"]; |
|
} |
|
|
|
/// 加密 |
|
- (NSString *)kj_md5:(NSString *)string { |
|
if (string == nil || [string length] == 0) return nil; |
|
unsigned char digest[CC_MD5_DIGEST_LENGTH], i; |
|
CC_MD5([string UTF8String], (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], digest); |
|
NSMutableString *ms = [NSMutableString string]; |
|
for (i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { |
|
[ms appendFormat:@"%02x", (int)(digest[i])]; |
|
} |
|
return [ms copy]; |
|
} |
|
|
|
@end |
|
|
|
@interface KJLoadImageView (){ |
|
__weak KJImageDownloader *_imageDownloader; |
|
} |
|
|
|
@end |
|
|
|
@implementation KJLoadImageView |
|
|
|
- (void)configureLayout { |
|
self.contentMode = UIViewContentModeScaleToFill; |
|
self.kj_failedTimes = 2; |
|
self.kj_isScale = NO; |
|
} |
|
- (instancetype)initWithFrame:(CGRect)frame { |
|
if (self = [super initWithFrame:frame]) { |
|
[self configureLayout]; |
|
} |
|
return self; |
|
} |
|
|
|
- (void)kj_setImageWithURLString:(NSString*)url Placeholder:(UIImage*)placeholderImage { |
|
return [self kj_setImageWithURLString:url Placeholder:placeholderImage Completion:nil]; |
|
} |
|
|
|
- (void)kj_setImageWithURLString:(NSString*)url Placeholder:(UIImage*)placeholderImage Completion:(void(^)(UIImage*image))completion { |
|
self.image = placeholderImage; |
|
self.kj_completionBlock = completion; |
|
if (url == nil || [url isKindOfClass:[NSNull class]] || (![url hasPrefix:@"http://"] && ![url hasPrefix:@"https://"])){ |
|
if (completion) self.kj_completionBlock(self.image); |
|
return; |
|
} |
|
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; |
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ |
|
[self kj_downloadWithReqeust:request holder:placeholderImage]; |
|
}); |
|
} |
|
|
|
#pragma mark - 内部方法 |
|
/// 下载图片 |
|
- (void)kj_downloadWithReqeust:(NSURLRequest*)theRequest holder:(UIImage*)holder { |
|
UIImage *cachedImage = [[UIApplication sharedApplication] kj_cacheImageForRequest:theRequest]; |
|
if (cachedImage) { |
|
dispatch_async(dispatch_get_main_queue(), ^{ |
|
self.image = cachedImage; |
|
}); |
|
if (self.kj_completionBlock) self.kj_completionBlock(cachedImage); |
|
return; |
|
} |
|
dispatch_async(dispatch_get_main_queue(), ^{ |
|
self.image = holder; |
|
}); |
|
/// 判断失败次数 |
|
if ([[UIApplication sharedApplication] kj_failTimesForRequest:theRequest] >= self.kj_failedTimes) { |
|
return; |
|
} |
|
|
|
[self kj_cancelRequest]; |
|
_imageDownloader = nil; |
|
|
|
__weak __typeof(self) weakSelf = self; |
|
KJImageDownloader *downloader = [[KJImageDownloader alloc] init]; |
|
_imageDownloader = downloader; |
|
[downloader kj_startDownloadImageWithUrl:theRequest.URL.absoluteString Progress:^(unsigned long long total, unsigned long long current) { |
|
!self.kj_progressBlock?:self.kj_progressBlock(total,current); |
|
} Complete:^(NSData *data, NSError *error) { |
|
if (data != nil && error == nil) {// 成功 |
|
UIImage *image = [UIImage imageWithData:data]; |
|
__block UIImage *finalImage = image; |
|
if (image) { |
|
dispatch_async(dispatch_get_main_queue(), ^{ |
|
if (weakSelf.kj_isScale) {// 剪裁 |
|
if (fabs(weakSelf.frame.size.width - image.size.width) != 0 && fabs(weakSelf.frame.size.height - image.size.height) != 0) { |
|
finalImage = [KJLoadImageView kj_clipImage:image Size:weakSelf.frame.size IsScaleToMax:YES]; |
|
} |
|
} |
|
}); |
|
[[UIApplication sharedApplication] kj_cacheImage:finalImage forRequest:theRequest]; |
|
} else { |
|
[[UIApplication sharedApplication] kj_cacheFailRequest:theRequest]; |
|
} |
|
if (finalImage) { |
|
dispatch_async(dispatch_get_main_queue(), ^{ |
|
weakSelf.image = finalImage; |
|
}); |
|
if (weakSelf.kj_completionBlock) { |
|
weakSelf.kj_completionBlock(weakSelf.image); |
|
} |
|
}else { |
|
if (weakSelf.kj_completionBlock) { |
|
weakSelf.kj_completionBlock(weakSelf.image); |
|
} |
|
} |
|
}else { |
|
[[UIApplication sharedApplication] kj_cacheFailRequest:theRequest]; |
|
if (weakSelf.kj_completionBlock) { |
|
weakSelf.kj_completionBlock(weakSelf.image); |
|
} |
|
} |
|
}]; |
|
} |
|
|
|
- (void)kj_cancelRequest { |
|
[_imageDownloader.task cancel]; |
|
} |
|
|
|
/// 裁剪图片 |
|
+ (UIImage*)kj_clipImage:(UIImage*)image Size:(CGSize)size IsScaleToMax:(BOOL)isScaleToMax { |
|
CGFloat scale = [UIScreen mainScreen].scale; |
|
UIGraphicsBeginImageContextWithOptions(size, NO, scale); |
|
CGSize aspectFitSize = CGSizeZero; |
|
if (image.size.width != 0 && image.size.height != 0) { |
|
CGFloat rateWidth = size.width / image.size.width; |
|
CGFloat rateHeight = size.height / image.size.height; |
|
CGFloat rate = isScaleToMax ? MAX(rateHeight, rateWidth) : MIN(rateHeight, rateWidth); |
|
aspectFitSize = CGSizeMake(image.size.width * rate, image.size.height * rate); |
|
} |
|
[image drawInRect:CGRectMake(0, 0, aspectFitSize.width, aspectFitSize.height)]; |
|
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext(); |
|
UIGraphicsEndImageContext(); |
|
return finalImage; |
|
} |
|
|
|
#pragma mark - 缓存相关方法 |
|
+ (void)kj_clearImagesCache { |
|
[[UIApplication sharedApplication] kj_clearDiskCaches]; |
|
} |
|
|
|
+ (unsigned long long)kj_imagesCacheSize { |
|
NSString *directoryPath = KJBannerLoadImages; |
|
BOOL isDir = NO; |
|
unsigned long long total = 0; |
|
if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:&isDir]) { |
|
if (isDir) { |
|
NSError *error = nil; |
|
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:&error]; |
|
if (error == nil) { |
|
for (NSString *subpath in array) { |
|
NSString *path = [directoryPath stringByAppendingPathComponent:subpath]; |
|
NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error]; |
|
if (!error) { |
|
total += [dict[NSFileSize] unsignedIntegerValue]; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
return total; |
|
} |
|
|
|
@end
|
|
|