Simplify creation of Photos Framework NSURLs in ASMultiplexImageNode

This commit is contained in:
Adlai Holler
2015-10-02 12:44:21 -07:00
parent 5f49e0f67d
commit de1f9788f3
5 changed files with 65 additions and 39 deletions

View File

@@ -9,6 +9,7 @@
#import <AsyncDisplayKit/ASImageNode.h> #import <AsyncDisplayKit/ASImageNode.h>
#import <AsyncDisplayKit/ASImageProtocols.h> #import <AsyncDisplayKit/ASImageProtocols.h>
#import <Photos/Photos.h>
@protocol ASMultiplexImageNodeDelegate; @protocol ASMultiplexImageNodeDelegate;
@protocol ASMultiplexImageNodeDataSource; @protocol ASMultiplexImageNodeDataSource;
@@ -98,6 +99,13 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
*/ */
@property (nonatomic, readonly) id displayedImageIdentifier; @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 @end
@@ -195,11 +203,32 @@ didFinishDownloadingImageWithIdentifier:(id)imageIdentifier
* @abstract An image URL for the specified identifier. * @abstract An image URL for the specified identifier.
* @param imageNode The sender. * @param imageNode The sender.
* @param imageIdentifier The identifier for the image that will be downloaded. * @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 * @discussion Supported URLs include HTTP, HTTPS, AssetsLibrary, and FTP URLs as well as Photos framework URLs (see note).
* image is already available to the data source, it should be provided via <[ASMultiplexImageNodeDataSource *
* If the image is already available to the data source, it should be provided via <[ASMultiplexImageNodeDataSource
* multiplexImageNode:imageForImageIdentifier:]> instead. * 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; - (NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode URLForImageIdentifier:(id)imageIdentifier;
@end @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

View File

@@ -157,7 +157,8 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
_cache = cache; _cache = cache;
_downloader = downloader; _downloader = downloader;
_imageManager = PHImageManager.defaultManager;
return self; 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. // 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) { [self _loadPHAssetWithRequest:request identifier:nextImageIdentifier completion:^(UIImage *image, NSError *error) {
ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from Photos Framework", weakSelf, nextImageIdentifier); ASMultiplexImageNodeCLogDebug(@"[%p] Acquired next image (%@) from Photos Framework", weakSelf, nextImageIdentifier);
finishedLoadingBlock(image, nextImageIdentifier, error); finishedLoadingBlock(image, nextImageIdentifier, error);
@@ -529,25 +530,27 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
PHAsset *imageAsset = [assetFetchResult firstObject]; PHAsset *imageAsset = [assetFetchResult firstObject];
PHImageRequestOptions *options = [request.options copy]; 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) { if (options.deliveryMode == PHImageRequestOptionsDeliveryModeHighQualityFormat) {
// Without this flag the result will be delivered on the main queue, which is pointless // 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 // But synchronous -> HighQualityFormat so we only use it if high quality format is specified
options.synchronous = YES; options.synchronous = YES;
} }
[[PHImageManager defaultManager] requestImageForAsset:imageAsset [self.imageManager requestImageForAsset:imageAsset targetSize:request.targetSize contentMode:request.contentMode options:options resultHandler:^(UIImage *image, NSDictionary *info) {
targetSize:request.targetSize if (NSThread.isMainThread) {
contentMode:request.contentMode dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
options:options completionBlock(image, info[PHImageErrorKey]);
resultHandler:^(UIImage *image, NSDictionary *info) { });
if (NSThread.isMainThread) { } else {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ completionBlock(image, info[PHImageErrorKey]);
completionBlock(image, info[PHImageErrorKey]); }
}); }];
} else {
completionBlock(image, info[PHImageErrorKey]);
}
}];
}); });
} }
@@ -648,3 +651,16 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
} }
@end @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

View File

@@ -48,8 +48,6 @@ extern NSString *const ASPhotosURLScheme;
@discussion Some properties of this object are ignored when converting this request into a URL. @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`. 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; @property (nonatomic, strong) PHImageRequestOptions *options;
@@ -65,13 +63,4 @@ extern NSString *const ASPhotosURLScheme;
@end @end
@interface NSURL (ASPhotosRequestConverting)
/**
@abstract A convenience function that calls `[ASPhotosFrameworkImageRequest requestWithURL:self]`.
*/
- (/*nullable*/ ASPhotosFrameworkImageRequest *)asyncdisplaykit_photosRequest;
@end
// NS_ASSUME_NONNULL_END // NS_ASSUME_NONNULL_END

View File

@@ -158,12 +158,3 @@ static NSString *const _ASPhotosURLQueryKeyCropHeight = @"crop_h";
} }
@end @end
@implementation NSURL (ASPhotosRequestConverting)
- (ASPhotosFrameworkImageRequest *)asyncdisplaykit_photosRequest
{
return [ASPhotosFrameworkImageRequest requestWithURL:self];
}
@end

View File

@@ -46,7 +46,8 @@ static NSString *const kTestAssetID = @"testAssetID";
- (void)testThatParsingFromURLWorks - (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 - (void)testThatCopyingWorks