diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b69fcd8fd..1f086bd71f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) - [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) +- Add support for URLs on ASNetworkImageNode. [Garrett Moon](https://github.com/garrettmoon) ##2.4 - Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler) diff --git a/Cartfile b/Cartfile index 93ba674369..bdcfff60c9 100644 --- a/Cartfile +++ b/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" diff --git a/Source/ASNetworkImageNode.h b/Source/ASNetworkImageNode.h index a438b422a3..dc911bede6 100644 --- a/Source/ASNetworkImageNode.h +++ b/Source/ASNetworkImageNode.h @@ -82,6 +82,15 @@ NS_ASSUME_NONNULL_BEGIN */ @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 () image + * while loading and the final image after the new image data was downloaded and processed. + */ +@property (nullable, nonatomic, strong, readwrite) NSArray *URLs; + /** * Download and display a new image. * diff --git a/Source/ASNetworkImageNode.mm b/Source/ASNetworkImageNode.mm index 25f31ae077..0e9c1fb1f9 100755 --- a/Source/ASNetworkImageNode.mm +++ b/Source/ASNetworkImageNode.mm @@ -39,7 +39,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; // Only access any of these with __instanceLock__. __weak id _delegate; - NSURL *_URL; + NSArray *_URLs; UIImage *_defaultImage; NSUUID *_cacheUUID; @@ -68,6 +68,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; unsigned int downloaderImplementsSetPriority:1; unsigned int downloaderImplementsAnimatedImage:1; unsigned int downloaderImplementsCancelWithResume:1; + unsigned int downloaderImplementsDownloadURLs:1; } _downloaderFlags; // 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 { unsigned int cacheSupportsClearing:1; unsigned int cacheSupportsSynchronousFetch:1; + unsigned int cacheSupportsCachedURLs:1; } _cacheFlags; } @@ -96,9 +98,11 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; _downloaderFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)]; _downloaderFlags.downloaderImplementsAnimatedImage = [downloader respondsToSelector:@selector(animatedImageWithData:)]; _downloaderFlags.downloaderImplementsCancelWithResume = [downloader respondsToSelector:@selector(cancelImageDownloadWithResumePossibilityForIdentifier:)]; + _downloaderFlags.downloaderImplementsDownloadURLs = [downloader respondsToSelector:@selector(downloadImageWithURLs:callbackQueue:downloadProgress:completion:)]; _cacheFlags.cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)]; _cacheFlags.cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)]; + _cacheFlags.cacheSupportsCachedURLs = [cache respondsToSelector:@selector(cachedImageWithURLs:callbackQueue:completion:)]; _shouldCacheImage = YES; _shouldRenderProgressImages = YES; @@ -136,8 +140,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; BOOL shouldCancelAndClear = imageWasSetExternally && (imageWasSetExternally != _imageWasSetExternally); _imageWasSetExternally = imageWasSetExternally; 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."); - _URL = nil; + 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."); + _URLs = nil; [self _locked_cancelDownloadAndClearImageWithResumePossibility:NO]; } @@ -158,15 +162,38 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; - (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 +{ + if (URL) { + [self setURLs:@[URL] resetToDefault:reset]; + } else { + [self setURLs:nil resetToDefault:reset]; + } +} + +- (NSURL *)URL +{ + return [self.URLs lastObject]; +} + +- (void)setURLs:(NSArray *)URLs +{ + [self setURLs:URLs resetToDefault:YES]; +} + +- (void)setURLs:(NSArray *)URLs resetToDefault:(BOOL)reset { { ASDN::MutexLocker l(__instanceLock__); - if (ASObjectIsEqual(URL, _URL)) { + if (ASObjectIsEqual(URLs, _URLs)) { return; } @@ -175,25 +202,25 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; _imageWasSetExternally = NO; [self _locked_cancelImageDownloadWithResumePossibility:NO]; - + _imageLoaded = NO; - _URL = URL; + _URLs = URLs; - BOOL hasURL = (_URL == nil); + BOOL hasURL = (_URLs.count == 0); if (reset || hasURL) { [self _locked_setCurrentImageQuality:(hasURL ? 0.0 : 1.0)]; [self _locked__setImage:_defaultImage]; } } - + [self setNeedsPreload]; } -- (NSURL *)URL +- (NSArray *)URLs { ASDN::MutexLocker l(__instanceLock__); - return _URL; + return _URLs; } - (void)setDefaultImage:(UIImage *)defaultImage @@ -212,7 +239,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; _defaultImage = defaultImage; 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]; } @@ -310,7 +337,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; - (BOOL)placeholderShouldPersist { 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 @@ -322,13 +349,16 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; if (asynchronously == NO && _cacheFlags.cacheSupportsSynchronousFetch) { ASDN::MutexLocker l(__instanceLock__); - if (_imageLoaded == NO && _URL && _downloadIdentifier == nil) { - UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:_URL] asdk_image]; - if (result) { - [self _locked_setCurrentImageQuality:1.0]; - [self _locked__setImage:result]; - - _imageLoaded = YES; + if (_imageLoaded == NO && _URLs.count > 0 && _downloadIdentifier == nil) { + for (NSURL *url in [_URLs reverseObjectEnumerator]) { + UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:url] asdk_image]; + if (result) { + [self _locked_setCurrentImageQuality:1.0]; + [self _locked__setImage:result]; + + _imageLoaded = YES; + break; + } } } } @@ -510,9 +540,11 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; _imageLoaded = NO; if (_cacheFlags.cacheSupportsClearing) { - if (_URL != nil) { - as_log_verbose(ASImageLoadingLog(), "Clearing cached image for %@ url: %@", self, _URL); - [_cache clearFetchedImageFromCacheWithURL:_URL]; + if (_URLs.count != 0) { + as_log_verbose(ASImageLoadingLog(), "Clearing cached image for %@ url: %@", self, _URLs); + for (NSURL *url in _URLs) { + [_cache clearFetchedImageFromCacheWithURL:url]; + } } } } @@ -546,7 +578,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; - (void)_downloadImageWithCompletion:(void (^)(id imageContainer, NSError*, id downloadIdentifier))finished { ASPerformBlockOnBackgroundThread(^{ - NSURL *url; + NSArray *urls; id downloadIdentifier; BOOL cancelAndReattempt = NO; @@ -555,23 +587,34 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; // it and try again. { ASDN::MutexLocker l(__instanceLock__); - url = _URL; + urls = _URLs; } - - downloadIdentifier = [_downloader downloadImageWithURL:url - callbackQueue:dispatch_get_main_queue() - downloadProgress:NULL - completion:^(id _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) { - if (finished != NULL) { - finished(imageContainer, error, downloadIdentifier); - } - }]; + if (_downloaderFlags.downloaderImplementsDownloadURLs) { + downloadIdentifier = [_downloader downloadImageWithURLs:urls + callbackQueue:dispatch_get_main_queue() + downloadProgress:NULL + completion:^(id _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) { + if (finished != NULL) { + finished(imageContainer, error, downloadIdentifier); + } + }]; + } else { + downloadIdentifier = [_downloader downloadImageWithURL:[urls lastObject] + callbackQueue:dispatch_get_main_queue() + downloadProgress:NULL + completion:^(id _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); { 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. _downloadIdentifier = downloadIdentifier; } else { @@ -600,34 +643,36 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; __weak id delegate = _delegate; BOOL delegateDidStartFetchingData = _delegateFlags.delegateDidStartFetchingData; BOOL isImageLoaded = _imageLoaded; - NSURL *URL = _URL; + NSArray *URLs = _URLs; id currentDownloadIdentifier = _downloadIdentifier; __instanceLock__.unlock(); - if (!isImageLoaded && URL != nil && currentDownloadIdentifier == nil) { + if (!isImageLoaded && URLs.count > 0 && currentDownloadIdentifier == nil) { if (delegateDidStartFetchingData) { [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(), ^{ ASDN::MutexLocker l(__instanceLock__); // Bail out if not the same URL anymore - if (!ASObjectIsEqual(URL, _URL)) { + if (!ASObjectIsEqual(URLs, _URLs)) { return; } + NSURL *URL = [URLs lastObject]; if (_shouldCacheImage) { - [self _locked__setImage:[UIImage imageNamed:_URL.path.lastPathComponent]]; + [self _locked__setImage:[UIImage imageNamed:URL.path.lastPathComponent]]; } else { // 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. - UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:_URL.path]; + UIImage *nonAnimatedImage = [UIImage imageWithContentsOfFile:URL.path]; if (nonAnimatedImage == nil) { // 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. - NSString *filename = [[NSBundle mainBundle] pathForResource:_URL.path.lastPathComponent ofType:nil]; + NSString *filename = [[NSBundle mainBundle] pathForResource:URL.path.lastPathComponent ofType:nil]; if (filename != nil) { 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. id animatedImage = nil; if (_downloaderFlags.downloaderImplementsAnimatedImage) { - NSData *data = [NSData dataWithContentsOfURL:_URL]; + NSData *data = [NSData dataWithContentsOfURL:URL]; if (data != nil) { animatedImage = [_downloader animatedImageWithData:data]; @@ -671,7 +716,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; 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 ASDN::MutexLocker l(strongSelf->__instanceLock__); @@ -714,26 +759,35 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; _cacheUUID = cacheUUID; __instanceLock__.unlock(); - as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ url: %@", self, URL); - [_cache cachedImageWithURL:URL - callbackQueue:dispatch_get_main_queue() - completion:^(id imageContainer) { - // If the cache UUID changed, that means this request was cancelled. - __instanceLock__.lock(); - NSUUID *currentCacheUUID = _cacheUUID; - __instanceLock__.unlock(); - - if (!ASObjectIsEqual(currentCacheUUID, cacheUUID)) { - return; - } - - if ([imageContainer asdk_image] == nil && _downloader != nil) { - [self _downloadImageWithCompletion:finished]; - } else { - as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL); - finished(imageContainer, nil, nil); - } - }]; + as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ urls: %@", self, URLs); + + ASImageCacherCompletion completion = ^(id imageContainer) { + // If the cache UUID changed, that means this request was cancelled. + __instanceLock__.lock(); + NSUUID *currentCacheUUID = _cacheUUID; + __instanceLock__.unlock(); + + if (!ASObjectIsEqual(currentCacheUUID, cacheUUID)) { + return; + } + + if ([imageContainer asdk_image] == nil && _downloader != nil) { + [self _downloadImageWithCompletion:finished]; + } else { + as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ urls: %@", self, [imageContainer asdk_image], URLs); + 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 { [self _downloadImageWithCompletion:finished]; } diff --git a/Source/Details/ASImageProtocols.h b/Source/Details/ASImageProtocols.h index 3fdf321e45..bc671866ac 100644 --- a/Source/Details/ASImageProtocols.h +++ b/Source/Details/ASImageProtocols.h @@ -37,7 +37,7 @@ typedef void(^ASImageCacherCompletion)(id _Nullable i @param URL The URL 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 `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. */ - (void)cachedImageWithURL:(NSURL *)URL @@ -66,6 +66,19 @@ typedef void(^ASImageCacherCompletion)(id _Nullable i */ - (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 *)URLs + callbackQueue:(dispatch_queue_t)callbackQueue + completion:(ASImageCacherCompletion)completion; + @end /** @@ -154,6 +167,21 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) { - (void)setPriority:(ASImageDownloaderPriority)priority 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 *)URLs + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion; + @end @protocol ASAnimatedImageProtocol diff --git a/Source/Details/ASPINRemoteImageDownloader.m b/Source/Details/ASPINRemoteImageDownloader.m index 3fd3c86dc0..0e6c6adff4 100644 --- a/Source/Details/ASPINRemoteImageDownloader.m +++ b/Source/Details/ASPINRemoteImageDownloader.m @@ -196,6 +196,23 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil; } } +- (void)cachedImageWithURLs:(NSArray *)URLs + callbackQueue:(dispatch_queue_t)callbackQueue + completion:(ASImageCacherCompletion)completion +{ + [self cachedImageWithURL:[URLs lastObject] + callbackQueue:callbackQueue + completion:^(id _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 { if ([self sharedImageManagerSupportsMemoryRemoval]) { @@ -210,43 +227,63 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil; downloadProgress:(ASImageDownloaderProgress)downloadProgress completion:(ASImageDownloaderCompletion)completion; { - return [[self sharedPINRemoteImageManager] downloadImageWithURL:URL options:PINRemoteImageManagerDownloadOptionsSkipDecode progressDownload:^(int64_t completedBytes, int64_t totalBytes) { - if (downloadProgress == nil) { return; } + NSArray *URLs = nil; + 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. - if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) { - downloadProgress(completedBytes / (CGFloat)totalBytes); - } else { - dispatch_async(callbackQueue, ^{ - downloadProgress(completedBytes / (CGFloat)totalBytes); - }); - } - } completion:^(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 - 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); +- (nullable id)downloadImageWithURLs:(NSArray *)URLs + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion +{ + PINRemoteImageManagerProgressDownload progressDownload = ^(int64_t completedBytes, int64_t totalBytes) { + if (downloadProgress == nil) { return; } + + /// If we're targeting the main queue and we're on the main thread, call immediately. + if (ASDisplayNodeThreadIsMain() && callbackQueue == dispatch_get_main_queue()) { + downloadProgress(completedBytes / (CGFloat)totalBytes); } 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 - completion(result.image, result.error, result.UUID); + 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 { + 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 diff --git a/Texture.podspec b/Texture.podspec index 9c53d9f4c1..1fcc6cf070 100644 --- a/Texture.podspec +++ b/Texture.podspec @@ -45,7 +45,7 @@ Pod::Spec.new do |spec| end 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 'Texture/Core' end