From de1f9788f35b067ca70ab83784c5fa8c59b4bf3d Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Fri, 2 Oct 2015 12:44:21 -0700 Subject: [PATCH] Simplify creation of Photos Framework NSURLs in ASMultiplexImageNode --- AsyncDisplayKit/ASMultiplexImageNode.h | 35 ++++++++++++-- AsyncDisplayKit/ASMultiplexImageNode.mm | 46 +++++++++++++------ .../Details/ASPhotosFrameworkImageRequest.h | 11 ----- .../Details/ASPhotosFrameworkImageRequest.m | 9 ---- .../ASPhotosFrameworkImageRequestTests.m | 3 +- 5 files changed, 65 insertions(+), 39 deletions(-) diff --git a/AsyncDisplayKit/ASMultiplexImageNode.h b/AsyncDisplayKit/ASMultiplexImageNode.h index 5b811f6654..2a3ec7f821 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.h +++ b/AsyncDisplayKit/ASMultiplexImageNode.h @@ -9,6 +9,7 @@ #import #import +#import @protocol ASMultiplexImageNodeDelegate; @protocol ASMultiplexImageNodeDataSource; @@ -98,6 +99,13 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) { */ @property (nonatomic, readonly) id displayedImageIdentifier; +/** + * @abstract The image manager that this image node should use when requesting images from the Photos framework. Defaults to `PHImageManager.defaultManager`. + + * @see `+[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:]` below. + */ +@property (nonatomic, strong) PHImageManager *imageManager; + @end @@ -195,11 +203,32 @@ didFinishDownloadingImageWithIdentifier:(id)imageIdentifier * @abstract An image URL for the specified identifier. * @param imageNode The sender. * @param imageIdentifier The identifier for the image that will be downloaded. - * @discussion Supported URLs include assets-library, URLs converted from ASPhotosFrameworkImageRequest, HTTP, HTTPS, and FTP URLs. If the - * image is already available to the data source, it should be provided via <[ASMultiplexImageNodeDataSource + * @discussion Supported URLs include HTTP, HTTPS, AssetsLibrary, and FTP URLs as well as Photos framework URLs (see note). + * + * If the image is already available to the data source, it should be provided via <[ASMultiplexImageNodeDataSource * multiplexImageNode:imageForImageIdentifier:]> instead. - * @returns An NSURL for the image identified by `imageIdentifier`, or nil if none is available. + * @return An NSURL for the image identified by `imageIdentifier`, or nil if none is available. + * @see `+[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:]` below. */ - (NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode URLForImageIdentifier:(id)imageIdentifier; @end + +#pragma mark - + +@interface NSURL (ASPhotosFrameworkURLs) + +/** + * @abstract Create an NSURL that specifies an image from the Photos framework. + * + * @discussion When implementing `-multiplexImageNode:URLForImageIdentifier:`, you can return a URL + * created by this method and the image node will attempt to load the image from the Photos framework. + * @note The `synchronous` flag in `options` is ignored. + * @note The `Opportunistic` delivery mode is not supported and will be treated as `HighQualityFormat`. + */ ++ (NSURL *)URLWithAssetLocalIdentifier:(NSString *)assetLocalIdentifier + targetSize:(CGSize)targetSize + contentMode:(PHImageContentMode)contentMode + options:(PHImageRequestOptions *)options; + +@end \ No newline at end of file diff --git a/AsyncDisplayKit/ASMultiplexImageNode.mm b/AsyncDisplayKit/ASMultiplexImageNode.mm index 026c1680a6..aa65649a7f 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.mm +++ b/AsyncDisplayKit/ASMultiplexImageNode.mm @@ -157,7 +157,8 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent _cache = cache; _downloader = downloader; - + _imageManager = PHImageManager.defaultManager; + return self; } @@ -455,7 +456,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent }]; } // Likewise, if it's a iOS 8 Photo asset, we need to fetch it accordingly. - else if (ASPhotosFrameworkImageRequest *request = nextImageURL.asyncdisplaykit_photosRequest) { + else if (ASPhotosFrameworkImageRequest *request = [ASPhotosFrameworkImageRequest requestWithURL:nextImageURL]) { [self _loadPHAssetWithRequest:request identifier:nextImageIdentifier completion:^(UIImage *image, NSError *error) { ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from Photos Framework", weakSelf, nextImageIdentifier); finishedLoadingBlock(image, nextImageIdentifier, error); @@ -529,25 +530,27 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent PHAsset *imageAsset = [assetFetchResult firstObject]; PHImageRequestOptions *options = [request.options copy]; + + // We don't support opportunistic delivery – one request, one image. + if (options.deliveryMode == PHImageRequestOptionsDeliveryModeOpportunistic) { + options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; + } + if (options.deliveryMode == PHImageRequestOptionsDeliveryModeHighQualityFormat) { // Without this flag the result will be delivered on the main queue, which is pointless // But synchronous -> HighQualityFormat so we only use it if high quality format is specified options.synchronous = YES; } - [[PHImageManager defaultManager] requestImageForAsset:imageAsset - targetSize:request.targetSize - contentMode:request.contentMode - options:options - resultHandler:^(UIImage *image, NSDictionary *info) { - if (NSThread.isMainThread) { - dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ - completionBlock(image, info[PHImageErrorKey]); - }); - } else { - completionBlock(image, info[PHImageErrorKey]); - } - }]; + [self.imageManager requestImageForAsset:imageAsset targetSize:request.targetSize contentMode:request.contentMode options:options resultHandler:^(UIImage *image, NSDictionary *info) { + if (NSThread.isMainThread) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ + completionBlock(image, info[PHImageErrorKey]); + }); + } else { + completionBlock(image, info[PHImageErrorKey]); + } + }]; }); } @@ -648,3 +651,16 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent } @end + +@implementation NSURL (ASPhotosFrameworkURLs) + ++ (NSURL *)URLWithAssetLocalIdentifier:(NSString *)assetLocalIdentifier targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(PHImageRequestOptions *)options +{ + ASPhotosFrameworkImageRequest *request = [[ASPhotosFrameworkImageRequest alloc] initWithAssetIdentifier:assetLocalIdentifier]; + request.options = options; + request.contentMode = contentMode; + request.targetSize = targetSize; + return request.url; +} + +@end \ No newline at end of file diff --git a/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.h b/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.h index 4f55b0c401..7630fe2e84 100644 --- a/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.h +++ b/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.h @@ -48,8 +48,6 @@ extern NSString *const ASPhotosURLScheme; @discussion Some properties of this object are ignored when converting this request into a URL. As of iOS SDK 9.0, these properties are `progressHandler` and `synchronous`. - Note that ASMultiplexImageNode does not support PHImageRequestOptionsDeliveryModeOpportunistic - because it sends multiple images, and that mode will be replaced by PHImageRequestOptionsDeliveryModeHighQualityFormat */ @property (nonatomic, strong) PHImageRequestOptions *options; @@ -65,13 +63,4 @@ extern NSString *const ASPhotosURLScheme; @end -@interface NSURL (ASPhotosRequestConverting) - -/** - @abstract A convenience function that calls `[ASPhotosFrameworkImageRequest requestWithURL:self]`. - */ -- (/*nullable*/ ASPhotosFrameworkImageRequest *)asyncdisplaykit_photosRequest; - -@end - // NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.m b/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.m index dcf47957c6..0e4b4e66ac 100644 --- a/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.m +++ b/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.m @@ -158,12 +158,3 @@ static NSString *const _ASPhotosURLQueryKeyCropHeight = @"crop_h"; } @end - -@implementation NSURL (ASPhotosRequestConverting) - -- (ASPhotosFrameworkImageRequest *)asyncdisplaykit_photosRequest -{ - return [ASPhotosFrameworkImageRequest requestWithURL:self]; -} - -@end diff --git a/AsyncDisplayKitTests/ASPhotosFrameworkImageRequestTests.m b/AsyncDisplayKitTests/ASPhotosFrameworkImageRequestTests.m index 5c276706b4..0d34e1944e 100644 --- a/AsyncDisplayKitTests/ASPhotosFrameworkImageRequestTests.m +++ b/AsyncDisplayKitTests/ASPhotosFrameworkImageRequestTests.m @@ -46,7 +46,8 @@ static NSString *const kTestAssetID = @"testAssetID"; - (void)testThatParsingFromURLWorks { - XCTAssertEqualObjects([self.class urlForExampleImageRequest].asyncdisplaykit_photosRequest, [self.class exampleImageRequest]); + NSURL *url = [self.class urlForExampleImageRequest]; + XCTAssertEqualObjects([ASPhotosFrameworkImageRequest requestWithURL:url], [self.class exampleImageRequest]); } - (void)testThatCopyingWorks