Skip to content

Commit dba7093

Browse files
authored
Support for high resolution images (pinterest#308)
* Automatic scaling of images if URL contains _2x or _3x prefix * Remove export PINScaledImageForKey and use static * Update README for how to handle high resolution images * Fix macOS support for using UIImage instead of PINImage * Further improvements * Address comments * Add imageFromCacheWithURL:processorKey:options:completion: * Remove helper `imageFromCacheWithURL:` helper function * Add processorKey: to synchronousImageFromCacheWithURL: * Deprecate synchronousImageFromCacheWithCacheKey * Fix tests * Fix nullable warning
1 parent 5a8db42 commit dba7093

8 files changed

Lines changed: 225 additions & 27 deletions

File tree

Examples/Example/PINRemoteImage Tests/PINRemoteImage_Tests.m

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ - (NSURL *)veryLongURL
136136
return [NSURL URLWithString:@"https://placekitten.com/g/200/301?longarg=helloMomHowAreYouDoing.IamFineJustMovedToLiveWithANiceChapWeTravelTogetherInHisBlueBoxThroughSpaceAndTimeMaybeYouveMetHimAlready.YesterdayWeMetACultureOfPeopleWithTentaclesWhoSingWithAVeryCelestialVoice.SoGood.SeeYouSoon.MaybeYesterday.WhoKnows.XOXO"];
137137
}
138138

139+
//- (NSURL *)scaledImageURL
140+
//{
141+
// return [NSURL URLWithString:@"http://www.grenadabluewatersailing.com/wp-content/uploads/2014/09/[email protected]"];
142+
//}
143+
139144
#pragma mark - <PINURLSessionManagerDelegate>
140145

141146
- (void)didReceiveData:(NSData *)data forTask:(NSURLSessionTask *)task
@@ -420,6 +425,22 @@ - (void)testDecoding
420425
XCTAssert(encodedDrawTime / decodedDrawTime > 2, @"Drawing decoded image should be much faster");
421426
}
422427

428+
//- (void)testScalingWithURLPostfix
429+
//{
430+
//
431+
// XCTestExpectation *expectation = [self expectationWithDescription:@"Error on scaling"];
432+
// [self.imageManager downloadImageWithURL:[self scaledImageURL]
433+
// options:PINRemoteImageManagerDownloadOptionsNone
434+
// completion:^(PINRemoteImageManagerResult *result)
435+
// {
436+
// XCTAssert(result.image != nil);
437+
// XCTAssert(result.image.scale == 2.0);
438+
//
439+
// [expectation fulfill];
440+
// }];
441+
// [self waitForExpectationsWithTimeout:[self timeoutTimeInterval] handler:nil];
442+
//}
443+
423444
- (void)drawImage:(UIImage *)image
424445
{
425446
UIGraphicsBeginImageContext(image.size);
@@ -619,7 +640,7 @@ - (void)testImageFromCacheReturnsNilErrorForCacheMiss
619640
{
620641
XCTestExpectation *expectation = [self expectationWithDescription:@"Image from cache"];
621642

622-
[self.imageManager imageFromCacheWithCacheKey:[self.imageManager cacheKeyForURL:[self JPEGURL] processorKey:nil] options:PINRemoteImageManagerDownloadOptionsNone completion:^(PINRemoteImageManagerResult * _Nonnull result) {
643+
[self.imageManager imageFromCacheWithURL:[self JPEGURL] processorKey:nil options:PINRemoteImageManagerDownloadOptionsNone completion:^(PINRemoteImageManagerResult * _Nonnull result) {
623644
XCTAssert(result.image == nil, @"Image was found in cache");
624645
XCTAssert(result.error == nil, @"Error was returned in cache miss");
625646

Examples/Example/PINRemoteImage/PINRemoteImage-Info.plist

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@
2424
<string>1.0</string>
2525
<key>LSRequiresIPhoneOS</key>
2626
<true/>
27+
<key>NSAppTransportSecurity</key>
28+
<dict>
29+
<key>NSAllowsArbitraryLoads</key>
30+
<true/>
31+
<key>NSExceptionAllowsInsecureHTTPLoads</key>
32+
<true/>
33+
</dict>
2734
<key>UIMainStoryboardFile</key>
2835
<string>Main</string>
2936
<key>UIRequiredDeviceCapabilities</key>

PINRemoteImage/PINRemoteImage.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
68F0EA931CB32EC900F1FD41 /* PINAlternateRepresentationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 68F0EA8F1CB32EC900F1FD41 /* PINAlternateRepresentationProvider.m */; };
2323
68F0EA941CB32EC900F1FD41 /* PINRemoteImageMemoryContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 68F0EA901CB32EC900F1FD41 /* PINRemoteImageMemoryContainer.h */; };
2424
68F0EA951CB32EC900F1FD41 /* PINRemoteImageMemoryContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 68F0EA911CB32EC900F1FD41 /* PINRemoteImageMemoryContainer.m */; };
25+
693818D41E4D14480006014D /* PINImage+ScaledImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 693818D21E4D14480006014D /* PINImage+ScaledImage.h */; };
26+
693818D51E4D14480006014D /* PINImage+ScaledImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 693818D31E4D14480006014D /* PINImage+ScaledImage.m */; };
2527
9DD47F9C1C699F4B00F12CA0 /* PINButton+PINRemoteImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DD47F981C699F4B00F12CA0 /* PINButton+PINRemoteImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
2628
9DD47F9D1C699F4B00F12CA0 /* PINButton+PINRemoteImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DD47F991C699F4B00F12CA0 /* PINButton+PINRemoteImage.m */; };
2729
9DD47F9E1C699F4B00F12CA0 /* PINImageView+PINRemoteImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DD47F9A1C699F4B00F12CA0 /* PINImageView+PINRemoteImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -76,6 +78,8 @@
7678
68F0EA8F1CB32EC900F1FD41 /* PINAlternateRepresentationProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PINAlternateRepresentationProvider.m; sourceTree = "<group>"; };
7779
68F0EA901CB32EC900F1FD41 /* PINRemoteImageMemoryContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PINRemoteImageMemoryContainer.h; sourceTree = "<group>"; };
7880
68F0EA911CB32EC900F1FD41 /* PINRemoteImageMemoryContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PINRemoteImageMemoryContainer.m; sourceTree = "<group>"; };
81+
693818D21E4D14480006014D /* PINImage+ScaledImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PINImage+ScaledImage.h"; sourceTree = "<group>"; };
82+
693818D31E4D14480006014D /* PINImage+ScaledImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PINImage+ScaledImage.m"; sourceTree = "<group>"; };
7983
9DD47F981C699F4B00F12CA0 /* PINButton+PINRemoteImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PINButton+PINRemoteImage.h"; sourceTree = "<group>"; };
8084
9DD47F991C699F4B00F12CA0 /* PINButton+PINRemoteImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PINButton+PINRemoteImage.m"; sourceTree = "<group>"; };
8185
9DD47F9A1C699F4B00F12CA0 /* PINImageView+PINRemoteImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PINImageView+PINRemoteImage.h"; sourceTree = "<group>"; };
@@ -236,6 +240,8 @@
236240
F1B918DF1BCF23C800710963 /* NSData+ImageDetectors.m */,
237241
9DD47FA01C699FDC00F12CA0 /* PINImage+DecodedImage.h */,
238242
9DD47FA11C699FDC00F12CA0 /* PINImage+DecodedImage.m */,
243+
693818D21E4D14480006014D /* PINImage+ScaledImage.h */,
244+
693818D31E4D14480006014D /* PINImage+ScaledImage.m */,
239245
9DD47FA21C699FDC00F12CA0 /* PINImage+WebP.h */,
240246
9DD47FA31C699FDC00F12CA0 /* PINImage+WebP.m */,
241247
);
@@ -282,6 +288,7 @@
282288
F1B919111BCF23C900710963 /* PINRemoteImageCallbacks.h in Headers */,
283289
F1B9190E1BCF23C900710963 /* PINProgressiveImage.h in Headers */,
284290
6858C0751C9CC5BA00E420EB /* PINRemoteLock.h in Headers */,
291+
693818D41E4D14480006014D /* PINImage+ScaledImage.h in Headers */,
285292
F1B919181BCF23C900710963 /* PINRemoteImageManagerResult.h in Headers */,
286293
F1B9191A1BCF23C900710963 /* PINRemoteImageProcessorTask.h in Headers */,
287294
9DD47FA41C699FDC00F12CA0 /* PINImage+DecodedImage.h in Headers */,
@@ -379,6 +386,7 @@
379386
F1B9191B1BCF23C900710963 /* PINRemoteImageProcessorTask.m in Sources */,
380387
F1B9190D1BCF23C900710963 /* PINDataTaskOperation.m in Sources */,
381388
9DD47FA71C699FDC00F12CA0 /* PINImage+WebP.m in Sources */,
389+
693818D51E4D14480006014D /* PINImage+ScaledImage.m in Sources */,
382390
9DD47F9F1C699F4B00F12CA0 /* PINImageView+PINRemoteImage.m in Sources */,
383391
F1B919171BCF23C900710963 /* PINRemoteImageManager.m in Sources */,
384392
);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// UIImage+ScaledImage.h
3+
// Pods
4+
//
5+
// Created by Michael Schneider on 2/9/17.
6+
//
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
#if PIN_TARGET_IOS
12+
#import <UIKit/UIKit.h>
13+
#elif PIN_TARGET_MAC
14+
#import <Cocoa/Cocoa.h>
15+
#endif
16+
17+
#import "PINRemoteImageMacros.h"
18+
19+
@interface PINImage (PINScaledImage)
20+
21+
- (PINImage *)pin_scaledImageForKey:(NSString *)key;
22+
+ (PINImage *)pin_scaledImageForImage:(PINImage *)image withKey:(NSString *)key;
23+
24+
@end
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// UIImage+ScaledImage.m
3+
// Pods
4+
//
5+
// Created by Michael Schneider on 2/9/17.
6+
//
7+
//
8+
9+
#import "PINImage+ScaledImage.h"
10+
11+
static inline PINImage *PINScaledImageForKey(NSString * __nullable key, PINImage * __nullable image) {
12+
if (image == nil) {
13+
return nil;
14+
}
15+
16+
#if PIN_TARGET_IOS
17+
18+
NSCAssert(image.CGImage != NULL, @"CGImage should not be NULL");
19+
20+
CGFloat scale = 1.0;
21+
if (key.length >= 8) {
22+
NSRange range = [key rangeOfString:@"_2x."];
23+
if (range.location != NSNotFound) {
24+
scale = 2.0;
25+
}
26+
27+
range = [key rangeOfString:@"_3x."];
28+
if (range.location != NSNotFound) {
29+
scale = 3.0;
30+
}
31+
}
32+
33+
if (scale != image.scale) {
34+
return [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
35+
}
36+
37+
return image;
38+
39+
#elif PIN_TARGET_MAC
40+
return image;
41+
#endif
42+
}
43+
44+
@implementation PINImage (ScaledImage)
45+
46+
- (PINImage *)pin_scaledImageForKey:(NSString *)key
47+
{
48+
return PINScaledImageForKey(key, self);
49+
}
50+
51+
+ (PINImage *)pin_scaledImageForImage:(PINImage *)image withKey:(NSString *)key
52+
{
53+
return PINScaledImageForKey(key, image);
54+
}
55+
56+
@end

Pod/Classes/PINRemoteImageManager.h

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,8 @@ typedef void(^PINRemoteImageManagerProgressDownload)(int64_t completedBytes, int
438438
439439
@param data NSData with the raw image data.
440440
@param url NSURL where the image resides.
441-
@processorKey NSString key to uniquely identify processor and process. Will be used for caching processed images.
442-
@additionalCost NSUInteger the additional cost (for cache eviction purposes) to generate the processed image
441+
@param processorKey NSString key to uniquely identify processor and process. Will be used for caching processed images.
442+
@param additionalCost NSUInteger the additional cost (for cache eviction purposes) to generate the processed image
443443
444444
@return A BOOL indicating if the image was successfully added to the cache.
445445
*/
@@ -460,7 +460,7 @@ typedef void(^PINRemoteImageManagerProgressDownload)(int64_t completedBytes, int
460460
- (nonnull NSString *)cacheKeyForURL:(nonnull NSURL *)url processorKey:(nullable NSString *)processorKey;
461461

462462
/**
463-
@see imageFromCacheWithCacheKey:options:completion: instead
463+
@see imageFromCacheWithURL:processorKey:options:completion:
464464
@deprecated
465465
466466
@param cacheKey NSString key to look up image in the cache.
@@ -469,25 +469,46 @@ typedef void(^PINRemoteImageManagerProgressDownload)(int64_t completedBytes, int
469469
- (void)imageFromCacheWithCacheKey:(nonnull NSString *)cacheKey completion:(nonnull PINRemoteImageManagerImageCompletion)completion __attribute__((deprecated));
470470

471471
/**
472-
Directly get an image from the underlying cache.
473-
@see cacheKeyForURL:processorKey:
472+
@see imageFromCacheWithURL:processorKey:options:completion:
473+
@deprecated
474474
475475
@param cacheKey NSString key to look up image in the cache.
476476
@param options options will be used to determine if the cached image should be decompressed or FLAnimatedImages should be returned.
477477
@param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache.
478478
*/
479-
- (void)imageFromCacheWithCacheKey:(nonnull NSString *)cacheKey options:(PINRemoteImageManagerDownloadOptions)options completion:(nonnull PINRemoteImageManagerImageCompletion)completion;
479+
- (void)imageFromCacheWithCacheKey:(nonnull NSString *)cacheKey options:(PINRemoteImageManagerDownloadOptions)options completion:(nonnull PINRemoteImageManagerImageCompletion)completion __attribute__((deprecated));
480480

481481
/**
482-
Directly get an image from the underlying memory cache synchronously.
483-
@see cacheKeyForURL:processorKey:
482+
Directly get an image from the underlying cache.
483+
484+
@param url NSURL to look up image in the cache.
485+
@param processorKey NSString key to uniquely identify processor and process.
486+
@param options options will be used to determine if the cached image should be decompressed or FLAnimatedImages should be returned.
487+
@param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache.
488+
*/
489+
- (void)imageFromCacheWithURL:(nonnull NSURL *)url processorKey:(nullable NSString *)processorKey options:(PINRemoteImageManagerDownloadOptions)options completion:(nonnull PINRemoteImageManagerImageCompletion)completion;
490+
491+
/**
492+
@deprecated
493+
@see synchronousImageFromCacheWithURL:processorKey:options:
484494
485495
@param cacheKey NSString key to look up image in the cache.
486496
@param options options will be used to determine if the cached image should be decompressed or FLAnimatedImages should be returned.
487497
488498
@return A PINRemoteImageManagerResult
489499
*/
490-
- (nonnull PINRemoteImageManagerResult *)synchronousImageFromCacheWithCacheKey:(nonnull NSString *)cacheKey options:(PINRemoteImageManagerDownloadOptions)options;
500+
- (nonnull PINRemoteImageManagerResult *)synchronousImageFromCacheWithCacheKey:(nonnull NSString *)cacheKey options:(PINRemoteImageManagerDownloadOptions)options __attribute__((deprecated));
501+
502+
/**
503+
Directly get an image from the underlying memory cache synchronously.
504+
505+
@param url NSURL to look up image in the cache.
506+
@param processorKey NSString key to uniquely identify processor and process
507+
@param options options will be used to determine if the cached image should be decompressed or FLAnimatedImages should be returned.
508+
509+
@return A PINRemoteImageManagerResult
510+
*/
511+
- (nonnull PINRemoteImageManagerResult *)synchronousImageFromCacheWithURL:(nonnull NSURL *)url processorKey:(nullable NSString *)processorKey options:(PINRemoteImageManagerDownloadOptions)options;
491512

492513
/**
493514
Cancel a download. Canceling will only cancel the download if all other downloads are also canceled with their associated UUIDs. Canceling *does not* guarantee that your completion will not be called. You can use the UUID provided on the result object verify the completion you want called is being called.

0 commit comments

Comments
 (0)