mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
Adds support for specifying a quality indexed array of URLs (#557)
* Add support for downloading a set of URLs on ASNetworkImageNode * Should be building now; * Remove old unused code * Add a changelog message * Bump PINRemoteImage * Huy's comments
This commit is contained in:
parent
786963c6a9
commit
3c77d4a5da
@ -20,6 +20,7 @@
|
|||||||
- Fixed a memory corruption issue in the ASImageNode display system. [Adlai Holler](https://github.com/Adlai-Holler) [#555](https://github.com/TextureGroup/Texture/pull/555)
|
- Fixed a memory corruption issue in the ASImageNode display system. [Adlai Holler](https://github.com/Adlai-Holler) [#555](https://github.com/TextureGroup/Texture/pull/555)
|
||||||
- [Breaking] Rename ASCollectionGalleryLayoutSizeProviding to ASCollectionGalleryLayoutPropertiesProviding. Besides a fixed item size, it now can provide interitem and line spacings, as well as section inset [Huy Nguyen](https://github.com/nguyenhuy) [#496](https://github.com/TextureGroup/Texture/pull/496) [#533](https://github.com/TextureGroup/Texture/pull/533)
|
- [Breaking] Rename ASCollectionGalleryLayoutSizeProviding to ASCollectionGalleryLayoutPropertiesProviding. Besides a fixed item size, it now can provide interitem and line spacings, as well as section inset [Huy Nguyen](https://github.com/nguyenhuy) [#496](https://github.com/TextureGroup/Texture/pull/496) [#533](https://github.com/TextureGroup/Texture/pull/533)
|
||||||
- Deprecate `-[ASDisplayNode displayWillStart]` in favor of `-displayWillStartAsynchronously:` [Huy Nguyen](https://github.com/nguyenhuy) [536](https://github.com/TextureGroup/Texture/pull/536)
|
- Deprecate `-[ASDisplayNode displayWillStart]` in favor of `-displayWillStartAsynchronously:` [Huy Nguyen](https://github.com/nguyenhuy) [536](https://github.com/TextureGroup/Texture/pull/536)
|
||||||
|
- Add support for URLs on ASNetworkImageNode. [Garrett Moon](https://github.com/garrettmoon)
|
||||||
|
|
||||||
##2.4
|
##2.4
|
||||||
- Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler)
|
- Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||||
|
|||||||
2
Cartfile
2
Cartfile
@ -1,2 +1,2 @@
|
|||||||
github "pinterest/PINRemoteImage" "3.0.0-beta.11"
|
github "pinterest/PINRemoteImage" "3.0.0-beta.12"
|
||||||
github "pinterest/PINCache" "3.0.1-beta.5"
|
github "pinterest/PINCache" "3.0.1-beta.5"
|
||||||
|
|||||||
@ -82,6 +82,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
*/
|
*/
|
||||||
@property (nullable, nonatomic, strong, readwrite) NSURL *URL;
|
@property (nullable, nonatomic, strong, readwrite) NSURL *URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of URLs of increasing cost to download.
|
||||||
|
*
|
||||||
|
* @discussion By setting an array of URLs, the image property of this node will be managed internally. This means previously
|
||||||
|
* directly set images to the image property will be cleared out and replaced by the placeholder (<defaultImage>) image
|
||||||
|
* while loading and the final image after the new image data was downloaded and processed.
|
||||||
|
*/
|
||||||
|
@property (nullable, nonatomic, strong, readwrite) NSArray <NSURL *> *URLs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download and display a new image.
|
* Download and display a new image.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -39,7 +39,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
// Only access any of these with __instanceLock__.
|
// Only access any of these with __instanceLock__.
|
||||||
__weak id<ASNetworkImageNodeDelegate> _delegate;
|
__weak id<ASNetworkImageNodeDelegate> _delegate;
|
||||||
|
|
||||||
NSURL *_URL;
|
NSArray *_URLs;
|
||||||
UIImage *_defaultImage;
|
UIImage *_defaultImage;
|
||||||
|
|
||||||
NSUUID *_cacheUUID;
|
NSUUID *_cacheUUID;
|
||||||
@ -68,6 +68,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
unsigned int downloaderImplementsSetPriority:1;
|
unsigned int downloaderImplementsSetPriority:1;
|
||||||
unsigned int downloaderImplementsAnimatedImage:1;
|
unsigned int downloaderImplementsAnimatedImage:1;
|
||||||
unsigned int downloaderImplementsCancelWithResume:1;
|
unsigned int downloaderImplementsCancelWithResume:1;
|
||||||
|
unsigned int downloaderImplementsDownloadURLs:1;
|
||||||
} _downloaderFlags;
|
} _downloaderFlags;
|
||||||
|
|
||||||
// Immutable and set on init only. We don't need to lock in this case.
|
// Immutable and set on init only. We don't need to lock in this case.
|
||||||
@ -75,6 +76,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
struct {
|
struct {
|
||||||
unsigned int cacheSupportsClearing:1;
|
unsigned int cacheSupportsClearing:1;
|
||||||
unsigned int cacheSupportsSynchronousFetch:1;
|
unsigned int cacheSupportsSynchronousFetch:1;
|
||||||
|
unsigned int cacheSupportsCachedURLs:1;
|
||||||
} _cacheFlags;
|
} _cacheFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,9 +98,11 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
_downloaderFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)];
|
_downloaderFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)];
|
||||||
_downloaderFlags.downloaderImplementsAnimatedImage = [downloader respondsToSelector:@selector(animatedImageWithData:)];
|
_downloaderFlags.downloaderImplementsAnimatedImage = [downloader respondsToSelector:@selector(animatedImageWithData:)];
|
||||||
_downloaderFlags.downloaderImplementsCancelWithResume = [downloader respondsToSelector:@selector(cancelImageDownloadWithResumePossibilityForIdentifier:)];
|
_downloaderFlags.downloaderImplementsCancelWithResume = [downloader respondsToSelector:@selector(cancelImageDownloadWithResumePossibilityForIdentifier:)];
|
||||||
|
_downloaderFlags.downloaderImplementsDownloadURLs = [downloader respondsToSelector:@selector(downloadImageWithURLs:callbackQueue:downloadProgress:completion:)];
|
||||||
|
|
||||||
_cacheFlags.cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)];
|
_cacheFlags.cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)];
|
||||||
_cacheFlags.cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)];
|
_cacheFlags.cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)];
|
||||||
|
_cacheFlags.cacheSupportsCachedURLs = [cache respondsToSelector:@selector(cachedImageWithURLs:callbackQueue:completion:)];
|
||||||
|
|
||||||
_shouldCacheImage = YES;
|
_shouldCacheImage = YES;
|
||||||
_shouldRenderProgressImages = YES;
|
_shouldRenderProgressImages = YES;
|
||||||
@ -136,8 +140,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
BOOL shouldCancelAndClear = imageWasSetExternally && (imageWasSetExternally != _imageWasSetExternally);
|
BOOL shouldCancelAndClear = imageWasSetExternally && (imageWasSetExternally != _imageWasSetExternally);
|
||||||
_imageWasSetExternally = imageWasSetExternally;
|
_imageWasSetExternally = imageWasSetExternally;
|
||||||
if (shouldCancelAndClear) {
|
if (shouldCancelAndClear) {
|
||||||
ASDisplayNodeAssertNil(_URL, @"Directly setting an image on an ASNetworkImageNode causes it to behave like an ASImageNode instead of an ASNetworkImageNode. If this is what you want, set the URL to nil first.");
|
ASDisplayNodeAssert(_URLs == nil || _URLs.count == 0, @"Directly setting an image on an ASNetworkImageNode causes it to behave like an ASImageNode instead of an ASNetworkImageNode. If this is what you want, set the URL to nil first.");
|
||||||
_URL = nil;
|
_URLs = nil;
|
||||||
[self _locked_cancelDownloadAndClearImageWithResumePossibility:NO];
|
[self _locked_cancelDownloadAndClearImageWithResumePossibility:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,15 +162,38 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
|
|
||||||
- (void)setURL:(NSURL *)URL
|
- (void)setURL:(NSURL *)URL
|
||||||
{
|
{
|
||||||
[self setURL:URL resetToDefault:YES];
|
if (URL) {
|
||||||
|
[self setURLs:@[URL]];
|
||||||
|
} else {
|
||||||
|
[self setURLs:nil];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset
|
- (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset
|
||||||
|
{
|
||||||
|
if (URL) {
|
||||||
|
[self setURLs:@[URL] resetToDefault:reset];
|
||||||
|
} else {
|
||||||
|
[self setURLs:nil resetToDefault:reset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSURL *)URL
|
||||||
|
{
|
||||||
|
return [self.URLs lastObject];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setURLs:(NSArray <NSURL *> *)URLs
|
||||||
|
{
|
||||||
|
[self setURLs:URLs resetToDefault:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setURLs:(NSArray <NSURL *> *)URLs resetToDefault:(BOOL)reset
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
if (ASObjectIsEqual(URL, _URL)) {
|
if (ASObjectIsEqual(URLs, _URLs)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,25 +202,25 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
_imageWasSetExternally = NO;
|
_imageWasSetExternally = NO;
|
||||||
|
|
||||||
[self _locked_cancelImageDownloadWithResumePossibility:NO];
|
[self _locked_cancelImageDownloadWithResumePossibility:NO];
|
||||||
|
|
||||||
_imageLoaded = NO;
|
_imageLoaded = NO;
|
||||||
|
|
||||||
_URL = URL;
|
_URLs = URLs;
|
||||||
|
|
||||||
BOOL hasURL = (_URL == nil);
|
BOOL hasURL = (_URLs.count == 0);
|
||||||
if (reset || hasURL) {
|
if (reset || hasURL) {
|
||||||
[self _locked_setCurrentImageQuality:(hasURL ? 0.0 : 1.0)];
|
[self _locked_setCurrentImageQuality:(hasURL ? 0.0 : 1.0)];
|
||||||
[self _locked__setImage:_defaultImage];
|
[self _locked__setImage:_defaultImage];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self setNeedsPreload];
|
[self setNeedsPreload];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSURL *)URL
|
- (NSArray <NSURL *>*)URLs
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
return _URL;
|
return _URLs;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDefaultImage:(UIImage *)defaultImage
|
- (void)setDefaultImage:(UIImage *)defaultImage
|
||||||
@ -212,7 +239,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
_defaultImage = defaultImage;
|
_defaultImage = defaultImage;
|
||||||
|
|
||||||
if (!_imageLoaded) {
|
if (!_imageLoaded) {
|
||||||
[self _locked_setCurrentImageQuality:((_URL == nil) ? 0.0 : 1.0)];
|
[self _locked_setCurrentImageQuality:((_URLs.count == 0) ? 0.0 : 1.0)];
|
||||||
[self _locked__setImage:defaultImage];
|
[self _locked__setImage:defaultImage];
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -310,7 +337,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
- (BOOL)placeholderShouldPersist
|
- (BOOL)placeholderShouldPersist
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
return (self.image == nil && self.animatedImage == nil && _URL != nil);
|
return (self.image == nil && self.animatedImage == nil && _URLs.count != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* displayWillStartAsynchronously: in ASMultiplexImageNode has a very similar implementation. Changes here are likely necessary
|
/* displayWillStartAsynchronously: in ASMultiplexImageNode has a very similar implementation. Changes here are likely necessary
|
||||||
@ -322,13 +349,16 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
if (asynchronously == NO && _cacheFlags.cacheSupportsSynchronousFetch) {
|
if (asynchronously == NO && _cacheFlags.cacheSupportsSynchronousFetch) {
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
if (_imageLoaded == NO && _URL && _downloadIdentifier == nil) {
|
if (_imageLoaded == NO && _URLs.count > 0 && _downloadIdentifier == nil) {
|
||||||
UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:_URL] asdk_image];
|
for (NSURL *url in [_URLs reverseObjectEnumerator]) {
|
||||||
if (result) {
|
UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:url] asdk_image];
|
||||||
[self _locked_setCurrentImageQuality:1.0];
|
if (result) {
|
||||||
[self _locked__setImage:result];
|
[self _locked_setCurrentImageQuality:1.0];
|
||||||
|
[self _locked__setImage:result];
|
||||||
_imageLoaded = YES;
|
|
||||||
|
_imageLoaded = YES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,9 +540,11 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
_imageLoaded = NO;
|
_imageLoaded = NO;
|
||||||
|
|
||||||
if (_cacheFlags.cacheSupportsClearing) {
|
if (_cacheFlags.cacheSupportsClearing) {
|
||||||
if (_URL != nil) {
|
if (_URLs.count != 0) {
|
||||||
as_log_verbose(ASImageLoadingLog(), "Clearing cached image for %@ url: %@", self, _URL);
|
as_log_verbose(ASImageLoadingLog(), "Clearing cached image for %@ url: %@", self, _URLs);
|
||||||
[_cache clearFetchedImageFromCacheWithURL:_URL];
|
for (NSURL *url in _URLs) {
|
||||||
|
[_cache clearFetchedImageFromCacheWithURL:url];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -546,7 +578,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
- (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> imageContainer, NSError*, id downloadIdentifier))finished
|
- (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> imageContainer, NSError*, id downloadIdentifier))finished
|
||||||
{
|
{
|
||||||
ASPerformBlockOnBackgroundThread(^{
|
ASPerformBlockOnBackgroundThread(^{
|
||||||
NSURL *url;
|
NSArray <NSURL *> *urls;
|
||||||
id downloadIdentifier;
|
id downloadIdentifier;
|
||||||
BOOL cancelAndReattempt = NO;
|
BOOL cancelAndReattempt = NO;
|
||||||
|
|
||||||
@ -555,23 +587,34 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
// it and try again.
|
// it and try again.
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
url = _URL;
|
urls = _URLs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_downloaderFlags.downloaderImplementsDownloadURLs) {
|
||||||
downloadIdentifier = [_downloader downloadImageWithURL:url
|
downloadIdentifier = [_downloader downloadImageWithURLs:urls
|
||||||
callbackQueue:dispatch_get_main_queue()
|
callbackQueue:dispatch_get_main_queue()
|
||||||
downloadProgress:NULL
|
downloadProgress:NULL
|
||||||
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
|
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
|
||||||
if (finished != NULL) {
|
if (finished != NULL) {
|
||||||
finished(imageContainer, error, downloadIdentifier);
|
finished(imageContainer, error, downloadIdentifier);
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
} else {
|
||||||
|
downloadIdentifier = [_downloader downloadImageWithURL:[urls lastObject]
|
||||||
|
callbackQueue:dispatch_get_main_queue()
|
||||||
|
downloadProgress:NULL
|
||||||
|
completion:^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
|
||||||
|
if (finished != NULL) {
|
||||||
|
finished(imageContainer, error, downloadIdentifier);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url);
|
as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url);
|
||||||
|
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
if (ASObjectIsEqual(_URL, url)) {
|
if (ASObjectIsEqual(_URLs, urls)) {
|
||||||
// The download we kicked off is correct, no need to do any more work.
|
// The download we kicked off is correct, no need to do any more work.
|
||||||
_downloadIdentifier = downloadIdentifier;
|
_downloadIdentifier = downloadIdentifier;
|
||||||
} else {
|
} else {
|
||||||
@ -600,34 +643,36 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
__weak id<ASNetworkImageNodeDelegate> delegate = _delegate;
|
__weak id<ASNetworkImageNodeDelegate> delegate = _delegate;
|
||||||
BOOL delegateDidStartFetchingData = _delegateFlags.delegateDidStartFetchingData;
|
BOOL delegateDidStartFetchingData = _delegateFlags.delegateDidStartFetchingData;
|
||||||
BOOL isImageLoaded = _imageLoaded;
|
BOOL isImageLoaded = _imageLoaded;
|
||||||
NSURL *URL = _URL;
|
NSArray <NSURL *>*URLs = _URLs;
|
||||||
id currentDownloadIdentifier = _downloadIdentifier;
|
id currentDownloadIdentifier = _downloadIdentifier;
|
||||||
__instanceLock__.unlock();
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
if (!isImageLoaded && URL != nil && currentDownloadIdentifier == nil) {
|
if (!isImageLoaded && URLs.count > 0 && currentDownloadIdentifier == nil) {
|
||||||
if (delegateDidStartFetchingData) {
|
if (delegateDidStartFetchingData) {
|
||||||
[delegate imageNodeDidStartFetchingData:self];
|
[delegate imageNodeDidStartFetchingData:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (URL.isFileURL) {
|
// We only support file URLs if there is one URL currently
|
||||||
|
if (URLs.count == 1 && [URLs lastObject].isFileURL) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
ASDN::MutexLocker l(__instanceLock__);
|
ASDN::MutexLocker l(__instanceLock__);
|
||||||
|
|
||||||
// Bail out if not the same URL anymore
|
// Bail out if not the same URL anymore
|
||||||
if (!ASObjectIsEqual(URL, _URL)) {
|
if (!ASObjectIsEqual(URLs, _URLs)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSURL *URL = [URLs lastObject];
|
||||||
if (_shouldCacheImage) {
|
if (_shouldCacheImage) {
|
||||||
[self _locked__setImage:[UIImage imageNamed:_URL.path.lastPathComponent]];
|
[self _locked__setImage:[UIImage imageNamed:URL.path.lastPathComponent]];
|
||||||
} else {
|
} else {
|
||||||
// First try to load the path directly, for efficiency assuming a developer who
|
// First try to load the path directly, for efficiency assuming a developer who
|
||||||
// doesn't want caching is trying to be as minimal as possible.
|
// doesn't want caching is trying to be as minimal as possible.
|
||||||
UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:_URL.path];
|
UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:URL.path];
|
||||||
if (nonAnimatedImage == nil) {
|
if (nonAnimatedImage == nil) {
|
||||||
// If we couldn't find it, execute an -imageNamed:-like search so we can find resources even if the
|
// If we couldn't find it, execute an -imageNamed:-like search so we can find resources even if the
|
||||||
// extension is not provided in the path. This allows the same path to work regardless of shouldCacheImage.
|
// extension is not provided in the path. This allows the same path to work regardless of shouldCacheImage.
|
||||||
NSString *filename = [[NSBundle mainBundle] pathForResource:_URL.path.lastPathComponent ofType:nil];
|
NSString *filename = [[NSBundle mainBundle] pathForResource:URL.path.lastPathComponent ofType:nil];
|
||||||
if (filename != nil) {
|
if (filename != nil) {
|
||||||
nonAnimatedImage = [UIImage imageWithContentsOfFile:filename];
|
nonAnimatedImage = [UIImage imageWithContentsOfFile:filename];
|
||||||
}
|
}
|
||||||
@ -636,7 +681,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
// If the file may be an animated gif and then created an animated image.
|
// If the file may be an animated gif and then created an animated image.
|
||||||
id<ASAnimatedImageProtocol> animatedImage = nil;
|
id<ASAnimatedImageProtocol> animatedImage = nil;
|
||||||
if (_downloaderFlags.downloaderImplementsAnimatedImage) {
|
if (_downloaderFlags.downloaderImplementsAnimatedImage) {
|
||||||
NSData *data = [NSData dataWithContentsOfURL:_URL];
|
NSData *data = [NSData dataWithContentsOfURL:URL];
|
||||||
if (data != nil) {
|
if (data != nil) {
|
||||||
animatedImage = [_downloader animatedImageWithData:data];
|
animatedImage = [_downloader animatedImageWithData:data];
|
||||||
|
|
||||||
@ -671,7 +716,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL);
|
as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ urls: %@", self, [imageContainer asdk_image], URLs);
|
||||||
|
|
||||||
// Grab the lock for the rest of the block
|
// Grab the lock for the rest of the block
|
||||||
ASDN::MutexLocker l(strongSelf->__instanceLock__);
|
ASDN::MutexLocker l(strongSelf->__instanceLock__);
|
||||||
@ -714,26 +759,35 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
|
|||||||
_cacheUUID = cacheUUID;
|
_cacheUUID = cacheUUID;
|
||||||
__instanceLock__.unlock();
|
__instanceLock__.unlock();
|
||||||
|
|
||||||
as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ url: %@", self, URL);
|
as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ urls: %@", self, URLs);
|
||||||
[_cache cachedImageWithURL:URL
|
|
||||||
callbackQueue:dispatch_get_main_queue()
|
ASImageCacherCompletion completion = ^(id <ASImageContainerProtocol> imageContainer) {
|
||||||
completion:^(id <ASImageContainerProtocol> imageContainer) {
|
// If the cache UUID changed, that means this request was cancelled.
|
||||||
// If the cache UUID changed, that means this request was cancelled.
|
__instanceLock__.lock();
|
||||||
__instanceLock__.lock();
|
NSUUID *currentCacheUUID = _cacheUUID;
|
||||||
NSUUID *currentCacheUUID = _cacheUUID;
|
__instanceLock__.unlock();
|
||||||
__instanceLock__.unlock();
|
|
||||||
|
if (!ASObjectIsEqual(currentCacheUUID, cacheUUID)) {
|
||||||
if (!ASObjectIsEqual(currentCacheUUID, cacheUUID)) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
if ([imageContainer asdk_image] == nil && _downloader != nil) {
|
||||||
if ([imageContainer asdk_image] == nil && _downloader != nil) {
|
[self _downloadImageWithCompletion:finished];
|
||||||
[self _downloadImageWithCompletion:finished];
|
} else {
|
||||||
} else {
|
as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ urls: %@", self, [imageContainer asdk_image], URLs);
|
||||||
as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL);
|
finished(imageContainer, nil, nil);
|
||||||
finished(imageContainer, nil, nil);
|
}
|
||||||
}
|
};
|
||||||
}];
|
|
||||||
|
if (_cacheFlags.cacheSupportsCachedURLs) {
|
||||||
|
[_cache cachedImageWithURLs:URLs
|
||||||
|
callbackQueue:dispatch_get_main_queue()
|
||||||
|
completion:completion];
|
||||||
|
} else {
|
||||||
|
[_cache cachedImageWithURL:[URLs lastObject]
|
||||||
|
callbackQueue:dispatch_get_main_queue()
|
||||||
|
completion:completion];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
[self _downloadImageWithCompletion:finished];
|
[self _downloadImageWithCompletion:finished];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ typedef void(^ASImageCacherCompletion)(id <ASImageContainerProtocol> _Nullable i
|
|||||||
@param URL The URL of the image to retrieve from the cache.
|
@param URL The URL of the image to retrieve from the cache.
|
||||||
@param callbackQueue The queue to call `completion` on.
|
@param callbackQueue The queue to call `completion` on.
|
||||||
@param completion The block to be called when the cache has either hit or missed.
|
@param completion The block to be called when the cache has either hit or missed.
|
||||||
@discussion If `URL` is nil, `completion` will be invoked immediately with a nil image. This method should not block
|
@discussion If `URL` is nil, `completion` should be invoked immediately with a nil image. This method should not block
|
||||||
the calling thread as it is likely to be called from the main thread.
|
the calling thread as it is likely to be called from the main thread.
|
||||||
*/
|
*/
|
||||||
- (void)cachedImageWithURL:(NSURL *)URL
|
- (void)cachedImageWithURL:(NSURL *)URL
|
||||||
@ -66,6 +66,19 @@ typedef void(^ASImageCacherCompletion)(id <ASImageContainerProtocol> _Nullable i
|
|||||||
*/
|
*/
|
||||||
- (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL;
|
- (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
@abstract Attempts to fetch an image with the given URLs from the cache in reverse order.
|
||||||
|
@param URLs The URLs of the image to retrieve from the cache.
|
||||||
|
@param callbackQueue The queue to call `completion` on.
|
||||||
|
@param completion The block to be called when the cache has either hit or missed.
|
||||||
|
@discussion If `URLs` is nil or empty, `completion` should be invoked immediately with a nil image. This method should not block
|
||||||
|
the calling thread as it is likely to be called from the main thread.
|
||||||
|
@see downloadImageWithURLs:callbackQueue:downloadProgress:completion:
|
||||||
|
*/
|
||||||
|
- (void)cachedImageWithURLs:(NSArray <NSURL *> *)URLs
|
||||||
|
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||||
|
completion:(ASImageCacherCompletion)completion;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -154,6 +167,21 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
|
|||||||
- (void)setPriority:(ASImageDownloaderPriority)priority
|
- (void)setPriority:(ASImageDownloaderPriority)priority
|
||||||
withDownloadIdentifier:(id)downloadIdentifier;
|
withDownloadIdentifier:(id)downloadIdentifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
@abstract Downloads an image from a list of URLs depending on previously observed network speed conditions.
|
||||||
|
@param URLs An array of URLs ordered by the cost of downloading them, the URL at index 0 being the lowest cost.
|
||||||
|
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
|
||||||
|
@param downloadProgress The block to be invoked when the download of `URL` progresses.
|
||||||
|
@param completion The block to be invoked when the download has completed, or has failed.
|
||||||
|
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
|
||||||
|
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
|
||||||
|
retain the identifier if you wish to use it later.
|
||||||
|
*/
|
||||||
|
- (nullable id)downloadImageWithURLs:(NSArray <NSURL *> *)URLs
|
||||||
|
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||||
|
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
|
||||||
|
completion:(ASImageDownloaderCompletion)completion;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@protocol ASAnimatedImageProtocol <NSObject>
|
@protocol ASAnimatedImageProtocol <NSObject>
|
||||||
|
|||||||
@ -196,6 +196,23 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)cachedImageWithURLs:(NSArray <NSURL *> *)URLs
|
||||||
|
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||||
|
completion:(ASImageCacherCompletion)completion
|
||||||
|
{
|
||||||
|
[self cachedImageWithURL:[URLs lastObject]
|
||||||
|
callbackQueue:callbackQueue
|
||||||
|
completion:^(id<ASImageContainerProtocol> _Nullable imageFromCache) {
|
||||||
|
if (imageFromCache.asdk_image == nil && URLs.count > 1) {
|
||||||
|
[self cachedImageWithURLs:[URLs subarrayWithRange:NSMakeRange(0, URLs.count - 1)]
|
||||||
|
callbackQueue:callbackQueue
|
||||||
|
completion:completion];
|
||||||
|
} else {
|
||||||
|
completion(imageFromCache);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL
|
- (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL
|
||||||
{
|
{
|
||||||
if ([self sharedImageManagerSupportsMemoryRemoval]) {
|
if ([self sharedImageManagerSupportsMemoryRemoval]) {
|
||||||
@ -210,43 +227,63 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil;
|
|||||||
downloadProgress:(ASImageDownloaderProgress)downloadProgress
|
downloadProgress:(ASImageDownloaderProgress)downloadProgress
|
||||||
completion:(ASImageDownloaderCompletion)completion;
|
completion:(ASImageDownloaderCompletion)completion;
|
||||||
{
|
{
|
||||||
return [[self sharedPINRemoteImageManager] downloadImageWithURL:URL options:PINRemoteImageManagerDownloadOptionsSkipDecode progressDownload:^(int64_t completedBytes, int64_t totalBytes) {
|
NSArray <NSURL *>*URLs = nil;
|
||||||
if (downloadProgress == nil) { return; }
|
if (URL) {
|
||||||
|
URLs = @[URL];
|
||||||
|
}
|
||||||
|
return [self downloadImageWithURLs:URLs callbackQueue:callbackQueue downloadProgress:downloadProgress completion:completion];
|
||||||
|
}
|
||||||
|
|
||||||
/// If we're targeting the main queue and we're on the main thread, call immediately.
|
- (nullable id)downloadImageWithURLs:(NSArray <NSURL *> *)URLs
|
||||||
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||||
downloadProgress(completedBytes / (CGFloat)totalBytes);
|
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
|
||||||
} else {
|
completion:(ASImageDownloaderCompletion)completion
|
||||||
dispatch_async(callbackQueue, ^{
|
{
|
||||||
downloadProgress(completedBytes / (CGFloat)totalBytes);
|
PINRemoteImageManagerProgressDownload progressDownload = ^(int64_t completedBytes, int64_t totalBytes) {
|
||||||
});
|
if (downloadProgress == nil) { return; }
|
||||||
}
|
|
||||||
} completion:^(PINRemoteImageManagerResult * _Nonnull result) {
|
/// If we're targeting the main queue and we're on the main thread, call immediately.
|
||||||
/// If we're targeting the main queue and we're on the main thread, complete immediately.
|
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
||||||
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
downloadProgress(completedBytes / (CGFloat)totalBytes);
|
||||||
#if PIN_ANIMATED_AVAILABLE
|
|
||||||
if (result.alternativeRepresentation) {
|
|
||||||
completion(result.alternativeRepresentation, result.error, result.UUID);
|
|
||||||
} else {
|
|
||||||
completion(result.image, result.error, result.UUID);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
completion(result.image, result.error, result.UUID);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
dispatch_async(callbackQueue, ^{
|
|
||||||
#if PIN_ANIMATED_AVAILABLE
|
|
||||||
if (result.alternativeRepresentation) {
|
|
||||||
completion(result.alternativeRepresentation, result.error, result.UUID);
|
|
||||||
} else {
|
} else {
|
||||||
completion(result.image, result.error, result.UUID);
|
dispatch_async(callbackQueue, ^{
|
||||||
|
downloadProgress(completedBytes / (CGFloat)totalBytes);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PINRemoteImageManagerImageCompletion imageCompletion = ^(PINRemoteImageManagerResult * _Nonnull result) {
|
||||||
|
/// If we're targeting the main queue and we're on the main thread, complete immediately.
|
||||||
|
if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) {
|
||||||
|
#if PIN_ANIMATED_AVAILABLE
|
||||||
|
if (result.alternativeRepresentation) {
|
||||||
|
completion(result.alternativeRepresentation, result.error, result.UUID);
|
||||||
|
} else {
|
||||||
|
completion(result.image, result.error, result.UUID);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
completion(result.image, result.error, result.UUID);
|
completion(result.image, result.error, result.UUID);
|
||||||
#endif
|
#endif
|
||||||
});
|
} else {
|
||||||
}
|
dispatch_async(callbackQueue, ^{
|
||||||
}];
|
#if PIN_ANIMATED_AVAILABLE
|
||||||
|
if (result.alternativeRepresentation) {
|
||||||
|
completion(result.alternativeRepresentation, result.error, result.UUID);
|
||||||
|
} else {
|
||||||
|
completion(result.image, result.error, result.UUID);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
completion(result.image, result.error, result.UUID);
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return [[self sharedPINRemoteImageManager] downloadImageWithURLs:URLs
|
||||||
|
options:PINRemoteImageManagerDownloadOptionsSkipDecode
|
||||||
|
progressImage:nil
|
||||||
|
progressDownload:progressDownload
|
||||||
|
completion:imageCompletion];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier
|
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier
|
||||||
|
|||||||
@ -45,7 +45,7 @@ Pod::Spec.new do |spec|
|
|||||||
end
|
end
|
||||||
|
|
||||||
spec.subspec 'PINRemoteImage' do |pin|
|
spec.subspec 'PINRemoteImage' do |pin|
|
||||||
pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.11'
|
pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.12'
|
||||||
pin.dependency 'PINRemoteImage/PINCache'
|
pin.dependency 'PINRemoteImage/PINCache'
|
||||||
pin.dependency 'Texture/Core'
|
pin.dependency 'Texture/Core'
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user