Skip to content

Commit ee0c027

Browse files
committed
Build photos image request class and tests
1 parent c824644 commit ee0c027

4 files changed

Lines changed: 307 additions & 0 deletions

File tree

AsyncDisplayKit.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@
374374
B350625D1B0111740018CF92 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943141A1575670030A7D0 /* Photos.framework */; };
375375
B350625E1B0111780018CF92 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943121A1575630030A7D0 /* AssetsLibrary.framework */; };
376376
B350625F1B0111800018CF92 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058D09AF195D04C000B7D73C /* Foundation.framework */; };
377+
CC7FD9DE1BB5E962005CCB2B /* ASPhotosImageRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CC7FD9DC1BB5E962005CCB2B /* ASPhotosImageRequest.h */; settings = {ASSET_TAGS = (); }; };
378+
CC7FD9DF1BB5E962005CCB2B /* ASPhotosImageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9DD1BB5E962005CCB2B /* ASPhotosImageRequest.m */; settings = {ASSET_TAGS = (); }; };
379+
CC7FD9E11BB5F750005CCB2B /* ASPhotosImageRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9E01BB5F750005CCB2B /* ASPhotosImageRequestTests.m */; settings = {ASSET_TAGS = (); }; };
377380
D785F6621A74327E00291744 /* ASScrollNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D785F6601A74327E00291744 /* ASScrollNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
378381
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; };
379382
DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; };
@@ -619,6 +622,9 @@
619622
ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
620623
B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
621624
B35061DD1B010EDF0018CF92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../AsyncDisplayKit-iOS/Info.plist"; sourceTree = "<group>"; };
625+
CC7FD9DC1BB5E962005CCB2B /* ASPhotosImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosImageRequest.h; sourceTree = "<group>"; };
626+
CC7FD9DD1BB5E962005CCB2B /* ASPhotosImageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosImageRequest.m; sourceTree = "<group>"; };
627+
CC7FD9E01BB5F750005CCB2B /* ASPhotosImageRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosImageRequestTests.m; sourceTree = "<group>"; };
622628
D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = "<group>"; };
623629
D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = "<group>"; };
624630
D785F6611A74327E00291744 /* ASScrollNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollNode.m; sourceTree = "<group>"; };
@@ -801,6 +807,7 @@
801807
ACF6ED581B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.m */,
802808
242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */,
803809
29CDC2E11AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m */,
810+
CC7FD9E01BB5F750005CCB2B /* ASPhotosImageRequestTests.m */,
804811
296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */,
805812
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */,
806813
2911485B1A77147A005D0878 /* ASControlNodeTests.m */,
@@ -836,6 +843,8 @@
836843
058D09E1195D050800B7D73C /* Details */ = {
837844
isa = PBXGroup;
838845
children = (
846+
CC7FD9DC1BB5E962005CCB2B /* ASPhotosImageRequest.h */,
847+
CC7FD9DD1BB5E962005CCB2B /* ASPhotosImageRequest.m */,
839848
058D09E2195D050800B7D73C /* _ASDisplayLayer.h */,
840849
058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */,
841850
058D09E4195D050800B7D73C /* _ASDisplayView.h */,
@@ -1106,6 +1115,7 @@
11061115
9C8221951BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */,
11071116
9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */,
11081117
AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */,
1118+
CC7FD9DE1BB5E962005CCB2B /* ASPhotosImageRequest.h in Headers */,
11091119
ACF6ED2F1B17843500DA7C62 /* ASStackLayoutSpec.h in Headers */,
11101120
ACF6ED4E1B17847A00DA7C62 /* ASStackLayoutSpecUtilities.h in Headers */,
11111121
ACF6ED4F1B17847A00DA7C62 /* ASStackPositionedLayout.h in Headers */,
@@ -1493,6 +1503,7 @@
14931503
058D0A21195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Sources */,
14941504
205F0E101B371875007741D0 /* UICollectionViewLayout+ASConvenience.m in Sources */,
14951505
058D0A25195D050800B7D73C /* UIView+ASConvenience.m in Sources */,
1506+
CC7FD9DF1BB5E962005CCB2B /* ASPhotosImageRequest.m in Sources */,
14961507
);
14971508
runOnlyForDeploymentPostprocessing = 0;
14981509
};
@@ -1514,6 +1525,7 @@
15141525
056D21551ABCEF50001107EF /* ASImageNodeSnapshotTests.m in Sources */,
15151526
ACF6ED5E1B178DC700DA7C62 /* ASInsetLayoutSpecSnapshotTests.mm in Sources */,
15161527
ACF6ED601B178DC700DA7C62 /* ASLayoutSpecSnapshotTestsHelper.m in Sources */,
1528+
CC7FD9E11BB5F750005CCB2B /* ASPhotosImageRequestTests.m in Sources */,
15171529
052EE0661A159FEF002C6279 /* ASMultiplexImageNodeTests.m in Sources */,
15181530
058D0A3C195D057000B7D73C /* ASMutableAttributedStringBuilderTests.m in Sources */,
15191531
ACF6ED611B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm in Sources */,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// ASPhotosImageRequest.h
3+
// AsyncDisplayKit
4+
//
5+
// Created by Adlai Holler on 9/25/15.
6+
// Copyright © 2015 Facebook. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
#import <Photos/Photos.h>
11+
12+
// NS_ASSUME_NONNULL_BEGIN
13+
14+
extern NSString *const ASPhotosURLScheme;
15+
16+
/**
17+
@abstract Use ASPhotosImageRequest to encapsulate all the information needed to request an image from
18+
the Photos framework and store it in a URL.
19+
*/
20+
@interface ASPhotosImageRequest : NSObject <NSCopying>
21+
22+
- (instancetype)initWithAssetIdentifier:(NSString *)assetIdentifier NS_DESIGNATED_INITIALIZER;
23+
24+
/**
25+
@return A new image request deserialized from `url`, or nil if `url` is not a valid photos URL.
26+
*/
27+
+ (/*nullable*/ ASPhotosImageRequest *)requestWithURL:(NSURL *)url;
28+
29+
/**
30+
@abstract The asset identifier for this image request provided during initialization.
31+
*/
32+
@property (nonatomic, readonly) NSString *assetIdentifier;
33+
34+
/**
35+
@abstract The target size for this image request. Defaults to `PHImageManagerMaximumSize`.
36+
*/
37+
@property (nonatomic) CGSize targetSize;
38+
39+
/**
40+
@abstract The content mode for this image request. Defaults to `PHImageContentModeDefault`.
41+
42+
@see `PHImageManager`
43+
*/
44+
@property (nonatomic) PHImageContentMode contentMode;
45+
46+
/**
47+
@abstract The options specified for this request. Default value is the result of `[PHImageRequestOptions new]`.
48+
49+
@discussion Some properties of this object are ignored when converting this request into a URL.
50+
As of iOS SDK 9.0, these properties are `progressHandler`, `synchronous`, and `deliveryMode`.
51+
*/
52+
@property (nonatomic, strong) PHImageRequestOptions *options;
53+
54+
/**
55+
@return A new URL converted from this request.
56+
*/
57+
@property (nonatomic, readonly) NSURL *url;
58+
59+
/**
60+
@return `YES` if `object` is an equivalent image request, `NO` otherwise.
61+
*/
62+
- (BOOL)isEqual:(id)object;
63+
@end
64+
65+
@interface NSURL (ASPhotosRequestConverting)
66+
67+
/**
68+
@abstract A convenience function that calls `[ASPhotosImageRequest requestWithURL:self]`.
69+
*/
70+
- (/*nullable*/ ASPhotosImageRequest *)asyncdisplaykit_photosRequest;
71+
72+
@end
73+
74+
// NS_ASSUME_NONNULL_END
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
//
2+
// ASPhotosImageRequest.m
3+
// AsyncDisplayKit
4+
//
5+
// Created by Adlai Holler on 9/25/15.
6+
// Copyright © 2015 Facebook. All rights reserved.
7+
//
8+
9+
#import "ASPhotosImageRequest.h"
10+
#import "ASBaseDefines.h"
11+
12+
NSString *const ASPhotosURLScheme = @"ph";
13+
14+
static NSString *const _ASPhotosURLQueryKeyWidth = @"width";
15+
static NSString *const _ASPhotosURLQueryKeyHeight = @"height";
16+
17+
// value is PHImageContentMode value
18+
static NSString *const _ASPhotosURLQueryKeyContentMode = @"contentmode";
19+
20+
// value is PHImageRequestOptionsResizeMode value
21+
static NSString *const _ASPhotosURLQueryKeyResizeMode = @"resizemode";
22+
23+
// value is PHImageRequestOptionsVersion value
24+
static NSString *const _ASPhotosURLQueryKeyVersion = @"version";
25+
26+
// value is 0 or 1
27+
static NSString *const _ASPhotosURLQueryKeyAllowNetworkAccess = @"network";
28+
29+
static NSString *const _ASPhotosURLQueryKeyCropOriginX = @"crop_x";
30+
static NSString *const _ASPhotosURLQueryKeyCropOriginY = @"crop_y";
31+
static NSString *const _ASPhotosURLQueryKeyCropWidth = @"crop_w";
32+
static NSString *const _ASPhotosURLQueryKeyCropHeight = @"crop_h";
33+
34+
@implementation ASPhotosImageRequest
35+
36+
- (instancetype)init
37+
{
38+
ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER();
39+
self = [self initWithAssetIdentifier:@""];
40+
return nil;
41+
}
42+
43+
- (instancetype)initWithAssetIdentifier:(NSString *)assetIdentifier
44+
{
45+
self = [super init];
46+
if (self) {
47+
_assetIdentifier = assetIdentifier;
48+
_options = [PHImageRequestOptions new];
49+
_contentMode = PHImageContentModeDefault;
50+
_targetSize = PHImageManagerMaximumSize;
51+
}
52+
return self;
53+
}
54+
55+
#pragma mark NSCopying
56+
57+
- (id)copyWithZone:(NSZone *)zone
58+
{
59+
ASPhotosImageRequest *copy = [[ASPhotosImageRequest alloc] initWithAssetIdentifier:self.assetIdentifier];
60+
copy.options = [self.options copy];
61+
copy.targetSize = self.targetSize;
62+
copy.contentMode = self.contentMode;
63+
return copy;
64+
}
65+
66+
#pragma mark Converting to URL
67+
68+
- (NSURL *)url
69+
{
70+
NSURLComponents *comp = [NSURLComponents new];
71+
comp.scheme = ASPhotosURLScheme;
72+
comp.host = _assetIdentifier;
73+
NSMutableArray *queryItems = [NSMutableArray arrayWithObjects:
74+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyWidth value:@(_targetSize.width).stringValue],
75+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyHeight value:@(_targetSize.height).stringValue],
76+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyVersion value:@(_options.version).stringValue],
77+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyContentMode value:@(_contentMode).stringValue],
78+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyAllowNetworkAccess value:@(_options.networkAccessAllowed).stringValue],
79+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyResizeMode value:@(_options.resizeMode).stringValue]
80+
, nil];
81+
82+
CGRect cropRect = _options.normalizedCropRect;
83+
if (!CGRectIsEmpty(cropRect)) {
84+
[queryItems addObjectsFromArray:@[
85+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropOriginX value:@(cropRect.origin.x).stringValue],
86+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropOriginY value:@(cropRect.origin.y).stringValue],
87+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropWidth value:@(cropRect.size.width).stringValue],
88+
[NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropHeight value:@(cropRect.size.height).stringValue]
89+
]];
90+
}
91+
comp.queryItems = queryItems;
92+
return comp.URL;
93+
}
94+
95+
#pragma mark Converting from URL
96+
97+
+ (ASPhotosImageRequest *)requestWithURL:(NSURL *)url
98+
{
99+
NSURLComponents *comp = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
100+
// not a photos URL
101+
if (![comp.scheme isEqualToString:ASPhotosURLScheme]) {
102+
return nil;
103+
}
104+
105+
ASPhotosImageRequest *request = [[ASPhotosImageRequest alloc] initWithAssetIdentifier:url.host];
106+
107+
CGRect cropRect = CGRectZero;
108+
CGSize targetSize = PHImageManagerMaximumSize;
109+
for (NSURLQueryItem *item in comp.queryItems) {
110+
if ([_ASPhotosURLQueryKeyAllowNetworkAccess isEqualToString:item.name]) {
111+
request.options.networkAccessAllowed = item.value.boolValue;
112+
} else if ([_ASPhotosURLQueryKeyWidth isEqualToString:item.name]) {
113+
targetSize.width = item.value.doubleValue;
114+
} else if ([_ASPhotosURLQueryKeyHeight isEqualToString:item.name]) {
115+
targetSize.height = item.value.doubleValue;
116+
} else if ([_ASPhotosURLQueryKeyContentMode isEqualToString:item.name]) {
117+
request.contentMode = (PHImageContentMode)item.value.integerValue;
118+
} else if ([_ASPhotosURLQueryKeyVersion isEqualToString:item.name]) {
119+
request.options.version = (PHImageRequestOptionsVersion)item.value.integerValue;
120+
} else if ([_ASPhotosURLQueryKeyCropOriginX isEqualToString:item.name]) {
121+
cropRect.origin.x = item.value.doubleValue;
122+
} else if ([_ASPhotosURLQueryKeyCropOriginY isEqualToString:item.name]) {
123+
cropRect.origin.y = item.value.doubleValue;
124+
} else if ([_ASPhotosURLQueryKeyCropWidth isEqualToString:item.name]) {
125+
cropRect.size.width = item.value.doubleValue;
126+
} else if ([_ASPhotosURLQueryKeyCropHeight isEqualToString:item.name]) {
127+
cropRect.size.height = item.value.doubleValue;
128+
} else if ([_ASPhotosURLQueryKeyResizeMode isEqualToString:item.name]) {
129+
request.options.resizeMode = (PHImageRequestOptionsResizeMode)item.value.integerValue;
130+
}
131+
}
132+
request.targetSize = targetSize;
133+
request.options.normalizedCropRect = cropRect;
134+
return request;
135+
}
136+
137+
#pragma mark NSObject
138+
139+
- (BOOL)isEqual:(id)object
140+
{
141+
if (![object isKindOfClass:ASPhotosImageRequest.class]) {
142+
return NO;
143+
}
144+
ASPhotosImageRequest *other = object;
145+
return [other.assetIdentifier isEqualToString:self.assetIdentifier] &&
146+
other.contentMode == self.contentMode &&
147+
CGSizeEqualToSize(other.targetSize, self.targetSize) &&
148+
CGRectEqualToRect(other.options.normalizedCropRect, self.options.normalizedCropRect) &&
149+
other.options.resizeMode == self.options.resizeMode &&
150+
other.options.version == self.options.version;
151+
}
152+
153+
@end
154+
155+
@implementation NSURL (ASPhotosRequestConverting)
156+
157+
- (ASPhotosImageRequest *)asyncdisplaykit_photosRequest
158+
{
159+
return [ASPhotosImageRequest requestWithURL:self];
160+
}
161+
162+
@end
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// ASPhotosImageRequestTests.m
3+
// AsyncDisplayKit
4+
//
5+
// Created by Adlai Holler on 9/25/15.
6+
// Copyright © 2015 Facebook. All rights reserved.
7+
//
8+
9+
#import <XCTest/XCTest.h>
10+
#import "ASPhotosImageRequest.h"
11+
12+
static NSString *const kTestAssetID = @"testAssetID";
13+
14+
@interface ASPhotosImageRequestTests : XCTestCase
15+
16+
@end
17+
18+
@implementation ASPhotosImageRequestTests
19+
20+
#pragma mark Example Data
21+
22+
+ (ASPhotosImageRequest *)exampleImageRequest
23+
{
24+
ASPhotosImageRequest *req = [[ASPhotosImageRequest alloc] initWithAssetIdentifier:kTestAssetID];
25+
req.options.networkAccessAllowed = YES;
26+
req.options.normalizedCropRect = CGRectMake(0.2, 0.1, 0.6, 0.8);
27+
req.targetSize = CGSizeMake(1024, 1536);
28+
req.contentMode = PHImageContentModeAspectFill;
29+
req.options.version = PHImageRequestOptionsVersionOriginal;
30+
req.options.resizeMode = PHImageRequestOptionsResizeModeFast;
31+
return req;
32+
}
33+
34+
+ (NSURL *)urlForExampleImageRequest
35+
{
36+
NSString *str = [NSString stringWithFormat:@"ph://%@?width=1024&height=1536&version=2&contentmode=1&network=1&resizemode=1&crop_x=0.2&crop_y=0.1&crop_w=0.6&crop_h=0.8", kTestAssetID];
37+
return [NSURL URLWithString:str];
38+
}
39+
40+
#pragma mark Test cases
41+
42+
- (void)testThatConvertingToURLWorks
43+
{
44+
XCTAssertEqualObjects([self.class exampleImageRequest].url, [self.class urlForExampleImageRequest]);
45+
}
46+
47+
- (void)testThatParsingFromURLWorks
48+
{
49+
XCTAssertEqualObjects([self.class urlForExampleImageRequest].asyncdisplaykit_photosRequest, [self.class exampleImageRequest]);
50+
}
51+
52+
- (void)testThatCopyingWorks
53+
{
54+
ASPhotosImageRequest *example = [self.class exampleImageRequest];
55+
ASPhotosImageRequest *copy = [[self.class exampleImageRequest] copy];
56+
XCTAssertEqualObjects(example, copy);
57+
}
58+
59+
@end

0 commit comments

Comments
 (0)