Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
14
iphone/Maps/Core/WebImage/IMWMImageCache.h
Normal file
14
iphone/Maps/Core/WebImage/IMWMImageCache.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol IMWMImageCache
|
||||
|
||||
- (void)imageForKey:(NSString *)imageKey
|
||||
completion:(void (^)(UIImage * _Nullable image, NSError * _Nullable error))completion;
|
||||
- (void)setImage:(UIImage *)image forKey:(NSString *)imageKey;
|
||||
- (void)cleanup;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
12
iphone/Maps/Core/WebImage/IMWMImageCoder.h
Normal file
12
iphone/Maps/Core/WebImage/IMWMImageCoder.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol IMWMImageCoder
|
||||
|
||||
- (UIImage * _Nullable)imageWithData:(NSData *)data;
|
||||
- (NSData * _Nullable)dataFromImage:(UIImage *)image;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
21
iphone/Maps/Core/WebImage/IMWMWebImage.h
Normal file
21
iphone/Maps/Core/WebImage/IMWMWebImage.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol IMWMImageTask <NSObject>
|
||||
|
||||
- (void)cancel;
|
||||
|
||||
@end
|
||||
|
||||
typedef void (^MWMWebImageCompletion)(UIImage * _Nullable image, NSError * _Nullable error);
|
||||
|
||||
@protocol IMWMWebImage
|
||||
|
||||
- (id<IMWMImageTask>)imageWithUrl:(NSURL *)url
|
||||
completion:(MWMWebImageCompletion)completion;
|
||||
- (void)cleanup;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
13
iphone/Maps/Core/WebImage/MWMImageCache.h
Normal file
13
iphone/Maps/Core/WebImage/MWMImageCache.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#import "IMWMImageCache.h"
|
||||
#import "IMWMImageCoder.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MWMImageCache : NSObject <IMWMImageCache>
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithImageCoder:(id<IMWMImageCoder>)imageCoder;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
92
iphone/Maps/Core/WebImage/MWMImageCache.m
Normal file
92
iphone/Maps/Core/WebImage/MWMImageCache.m
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#import "MWMImageCache.h"
|
||||
#import "NSString+MD5.h"
|
||||
|
||||
static NSTimeInterval kCleanupTimeInterval = 30 * 24 * 60 * 60;
|
||||
|
||||
@interface MWMImageCache ()
|
||||
|
||||
@property (nonatomic, strong) NSCache<NSString *, UIImage *> *cache;
|
||||
@property (nonatomic, copy) NSString *cacheDirPath;
|
||||
@property (nonatomic, strong) dispatch_queue_t diskQueue;
|
||||
@property (nonatomic, strong) NSFileManager *fileManager;
|
||||
@property (nonatomic, strong) id<IMWMImageCoder> imageCoder;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMImageCache
|
||||
|
||||
- (instancetype)initWithImageCoder:(id<IMWMImageCoder>)imageCoder {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_cache = [[NSCache alloc] init];
|
||||
_cacheDirPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"images"];
|
||||
_diskQueue = dispatch_queue_create("mapsme.imageCache.disk", DISPATCH_QUEUE_SERIAL);
|
||||
_fileManager = [NSFileManager defaultManager];
|
||||
_imageCoder = imageCoder;
|
||||
|
||||
[_fileManager createDirectoryAtPath:_cacheDirPath
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)imageForKey:(NSString *)imageKey completion:(void (^)(UIImage *image, NSError *error))completion {
|
||||
UIImage *image = [self.cache objectForKey:imageKey];
|
||||
if (image) {
|
||||
completion(image, nil);
|
||||
} else {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSString *path = [self.cacheDirPath stringByAppendingPathComponent:imageKey.md5String];
|
||||
__block NSData *imageData = nil;
|
||||
__block NSError *error = nil;
|
||||
dispatch_sync(self.diskQueue, ^{
|
||||
imageData = [NSData dataWithContentsOfFile:path options:0 error:&error];
|
||||
});
|
||||
UIImage *image = nil;
|
||||
if (imageData) {
|
||||
image = [self.imageCoder imageWithData:imageData];
|
||||
if (image) {
|
||||
[self.cache setObject:image forKey:imageKey];
|
||||
}
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(image, error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image forKey:(NSString *)imageKey {
|
||||
[self.cache setObject:image forKey:imageKey];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSData *imageData = [self.imageCoder dataFromImage:image];
|
||||
if (imageData) {
|
||||
NSString *path = [self.cacheDirPath stringByAppendingPathComponent:imageKey.md5String];
|
||||
dispatch_sync(self.diskQueue, ^{
|
||||
[imageData writeToFile:path atomically:YES];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)cleanup {
|
||||
NSDirectoryEnumerator<NSString *> *enumerator = [self.fileManager enumeratorAtPath:self.cacheDirPath];
|
||||
for (NSString *fileName in enumerator) {
|
||||
NSString *path = [self.cacheDirPath stringByAppendingPathComponent:fileName];
|
||||
NSError *error = nil;
|
||||
NSDictionary *attributes = [self.fileManager attributesOfItemAtPath:path error:&error];
|
||||
if (!error) {
|
||||
NSDate *date = attributes[NSFileCreationDate];
|
||||
if (fabs(date.timeIntervalSinceNow) > kCleanupTimeInterval) {
|
||||
dispatch_sync(self.diskQueue, ^{
|
||||
[self.fileManager removeItemAtPath:path error:nil];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
9
iphone/Maps/Core/WebImage/MWMImageCoder.h
Normal file
9
iphone/Maps/Core/WebImage/MWMImageCoder.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#import "IMWMImageCoder.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MWMImageCoder : NSObject <IMWMImageCoder>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
51
iphone/Maps/Core/WebImage/MWMImageCoder.m
Normal file
51
iphone/Maps/Core/WebImage/MWMImageCoder.m
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#import "MWMImageCoder.h"
|
||||
|
||||
@implementation MWMImageCoder
|
||||
|
||||
- (UIImage *)imageWithData:(NSData *)data {
|
||||
UIImage *image = [UIImage imageWithData:data];
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGImageRef cgImage = image.CGImage;
|
||||
size_t width = CGImageGetWidth(cgImage);
|
||||
size_t height = CGImageGetHeight(cgImage);
|
||||
int32_t flags;
|
||||
if ([self imageHasAlpha:image]) {
|
||||
flags = kCGImageAlphaPremultipliedLast;
|
||||
} else {
|
||||
flags = kCGImageAlphaNoneSkipLast;
|
||||
}
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, flags);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
|
||||
CGImageRef resultCgImage = CGBitmapContextCreateImage(context);
|
||||
UIImage *resultImage = [UIImage imageWithCGImage:resultCgImage];
|
||||
|
||||
CGImageRelease(resultCgImage);
|
||||
CGContextRelease(context);
|
||||
|
||||
return resultImage;
|
||||
}
|
||||
|
||||
- (NSData *)dataFromImage:(UIImage *)image {
|
||||
if ([self imageHasAlpha:image]) {
|
||||
return UIImagePNGRepresentation(image);
|
||||
} else {
|
||||
return UIImageJPEGRepresentation(image, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)imageHasAlpha:(UIImage *)image {
|
||||
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(image.CGImage);
|
||||
return (alphaInfo == kCGImageAlphaPremultipliedLast ||
|
||||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
|
||||
alphaInfo == kCGImageAlphaLast ||
|
||||
alphaInfo == kCGImageAlphaFirst);
|
||||
}
|
||||
|
||||
@end
|
||||
22
iphone/Maps/Core/WebImage/MWMWebImage.h
Normal file
22
iphone/Maps/Core/WebImage/MWMWebImage.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#import "IMWMWebImage.h"
|
||||
#import "IMWMImageCache.h"
|
||||
#import "IMWMImageCoder.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MWMWebImage : NSObject <IMWMWebImage>
|
||||
|
||||
+ (MWMWebImage *)defaultWebImage;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithImageCahce:(id<IMWMImageCache>)imageCache
|
||||
imageCoder:(id<IMWMImageCoder>)imageCoder;
|
||||
- (id<IMWMImageTask>)imageWithUrl:(NSURL *)url
|
||||
completion:(MWMWebImageCompletion)completion;
|
||||
- (void)cleanup;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
108
iphone/Maps/Core/WebImage/MWMWebImage.m
Normal file
108
iphone/Maps/Core/WebImage/MWMWebImage.m
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#import "MWMWebImage.h"
|
||||
#import "MWMImageCache.h"
|
||||
#import "MWMImageCoder.h"
|
||||
|
||||
@interface MWMWebImageTask : NSObject <IMWMImageTask>
|
||||
|
||||
@property (nonatomic) BOOL cancelled;
|
||||
@property (nonatomic, strong) NSURLSessionTask *dataTask;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMWebImageTask
|
||||
|
||||
- (void)cancel {
|
||||
self.cancelled = YES;
|
||||
[self.dataTask cancel];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface MWMWebImage () <NSURLSessionDelegate>
|
||||
|
||||
@property (nonatomic, strong) NSURLSession *urlSession;
|
||||
@property (nonatomic, strong) id<IMWMImageCache> imageCache;
|
||||
@property (nonatomic, strong) id<IMWMImageCoder> imageCoder;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMWebImage
|
||||
|
||||
+ (MWMWebImage *)defaultWebImage {
|
||||
static MWMWebImage *instanse;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
MWMImageCoder *coder = [MWMImageCoder new];
|
||||
instanse = [[self alloc] initWithImageCahce:[[MWMImageCache alloc] initWithImageCoder:coder]
|
||||
imageCoder:coder];
|
||||
});
|
||||
return instanse;
|
||||
}
|
||||
|
||||
- (instancetype)initWithImageCahce:(id<IMWMImageCache>)imageCache
|
||||
imageCoder:(id<IMWMImageCoder>)imageCoder {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]
|
||||
delegate:self
|
||||
delegateQueue:nil];
|
||||
_imageCache = imageCache;
|
||||
_imageCoder = imageCoder;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<IMWMImageTask>)imageWithUrl:(NSURL *)url
|
||||
completion:(MWMWebImageCompletion)completion {
|
||||
MWMWebImageTask *imageTask = [MWMWebImageTask new];
|
||||
NSString *cacheKey = url.absoluteString;
|
||||
[self.imageCache imageForKey:cacheKey completion:^(UIImage *image, NSError *error) {
|
||||
if (imageTask.cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (image) {
|
||||
completion(image, nil);
|
||||
} else {
|
||||
NSURLSessionTask *dataTask = [self.urlSession dataTaskWithURL:url
|
||||
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
UIImage *image = nil;
|
||||
if (data) {
|
||||
image = [self.imageCoder imageWithData:data];
|
||||
if (image) {
|
||||
[self.imageCache setImage:image forKey:cacheKey];
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (!imageTask.cancelled) {
|
||||
completion(image, error);
|
||||
}
|
||||
});
|
||||
}];
|
||||
imageTask.dataTask = dataTask;
|
||||
[dataTask resume];
|
||||
}
|
||||
}];
|
||||
|
||||
return imageTask;
|
||||
}
|
||||
|
||||
- (void)cleanup {
|
||||
[self.imageCache cleanup];
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSessionDelegate
|
||||
|
||||
#if DEBUG
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
|
||||
NSURLCredential *credential))completionHandler
|
||||
{
|
||||
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
11
iphone/Maps/Core/WebImage/NSString+MD5.h
Normal file
11
iphone/Maps/Core/WebImage/NSString+MD5.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSString (MD5)
|
||||
|
||||
- (NSString *)md5String;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
23
iphone/Maps/Core/WebImage/NSString+MD5.m
Normal file
23
iphone/Maps/Core/WebImage/NSString+MD5.m
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#import "NSString+MD5.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
@implementation NSString (MD5)
|
||||
|
||||
- (NSString *)md5String {
|
||||
NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data.length == 0) {
|
||||
return @"";
|
||||
}
|
||||
|
||||
unsigned char buf[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5(data.bytes, (CC_LONG)data.length, buf);
|
||||
NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
|
||||
for (NSInteger i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) {
|
||||
[result appendFormat:@"%02x", buf[i]];
|
||||
}
|
||||
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
@end
|
||||
15
iphone/Maps/Core/WebImage/UIImageView+WebImage.h
Normal file
15
iphone/Maps/Core/WebImage/UIImageView+WebImage.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface UIImageView (WebImage)
|
||||
|
||||
- (void)wi_setImageWithUrl:(NSURL *)url;
|
||||
- (void)wi_setImageWithUrl:(NSURL *)url
|
||||
transitionDuration:(NSTimeInterval)duration
|
||||
completion:(void (^ _Nullable)(UIImage * _Nullable image, NSError * _Nullable error))completion;
|
||||
- (void)wi_cancelImageRequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
46
iphone/Maps/Core/WebImage/UIImageView+WebImage.m
Normal file
46
iphone/Maps/Core/WebImage/UIImageView+WebImage.m
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#import "UIImageView+WebImage.h"
|
||||
#import "MWMWebImage.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static char kAssociatedObjectKey;
|
||||
|
||||
@implementation UIImageView (WebImage)
|
||||
|
||||
- (void)wi_setImageWithUrl:(NSURL *)url {
|
||||
[self wi_setImageWithUrl:url transitionDuration:0 completion:nil];
|
||||
}
|
||||
|
||||
- (void)wi_setImageWithUrl:(NSURL *)url
|
||||
transitionDuration:(NSTimeInterval)duration
|
||||
completion:(void (^)(UIImage *, NSError *))completion {
|
||||
id<IMWMImageTask> task = [[MWMWebImage defaultWebImage] imageWithUrl:url
|
||||
completion:^(UIImage *image, NSError *error) {
|
||||
objc_setAssociatedObject(self, &kAssociatedObjectKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
if (!image) {
|
||||
if (completion) { completion(nil, error); }
|
||||
return;
|
||||
}
|
||||
|
||||
if (duration > 0) {
|
||||
[UIView transitionWithView:self
|
||||
duration:duration
|
||||
options:UIViewAnimationOptionTransitionCrossDissolve
|
||||
animations:^{ self.image = image; }
|
||||
completion:nil];
|
||||
} else {
|
||||
self.image = image;
|
||||
}
|
||||
|
||||
if (completion) { completion(image, nil); }
|
||||
}];
|
||||
objc_setAssociatedObject(self, &kAssociatedObjectKey, task, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
- (void)wi_cancelImageRequest {
|
||||
id<IMWMImageTask> task = objc_getAssociatedObject(self, &kAssociatedObjectKey);
|
||||
[task cancel];
|
||||
objc_setAssociatedObject(self, &kAssociatedObjectKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
@end
|
||||
Loading…
Add table
Add a link
Reference in a new issue