Skip to content

Commit

Permalink
add image size to onLoad nativeEvent
Browse files Browse the repository at this point in the history
add dimensions and scale properties to the nativeEvent

add comments

add check for images without intrinsic size

only show size for network images

clean up formatting

create event first, check for nil

change id dimensions to NSValue *dimensions

change to NSValue *dimensions

catch http images

remove file git included

add tests

fix tests

fix errors
  • Loading branch information
Luke Dubert committed Oct 20, 2015
1 parent 523769f commit 1e632f1
Show file tree
Hide file tree
Showing 14 changed files with 48 additions and 34 deletions.
1 change: 1 addition & 0 deletions Examples/UIExplorer/ImageExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ exports.examples = [
<Image
source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}
style={styles.base}
onLoad={(e) => console.log('woah', e.nativeEvent)}
/>
);
},
Expand Down
16 changes: 8 additions & 8 deletions Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ - (void)testImageLoading
return YES;
} loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) {
progressHandler(1, 1);
completionHandler(nil, image);
completionHandler(nil, image, nil);
return nil;
}];

Expand All @@ -53,7 +53,7 @@ - (void)testImageLoading
[imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
} completionBlock:^(NSError *loadError, id loadedImage, __unused NSValue *dimensions) {
XCTAssertEqualObjects(loadedImage, image);
XCTAssertNil(loadError);
}];
Expand All @@ -67,7 +67,7 @@ - (void)testImageLoaderUsesImageURLLoaderWithHighestPriority
return YES;
} loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) {
progressHandler(1, 1);
completionHandler(nil, image);
completionHandler(nil, image, nil);
return nil;
}];

Expand All @@ -84,7 +84,7 @@ - (void)testImageLoaderUsesImageURLLoaderWithHighestPriority
[imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
} completionBlock:^(NSError *loadError, id loadedImage, __unused NSValue *dimensions) {
XCTAssertEqualObjects(loadedImage, image);
XCTAssertNil(loadError);
}];
Expand All @@ -99,14 +99,14 @@ - (void)testImageDecoding
return YES;
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
XCTAssertEqualObjects(imageData, data);
completionHandler(nil, image);
completionHandler(nil, image, nil);
return nil;
}];

RCTImageLoader *imageLoader = [RCTImageLoader new];
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder, imageLoader]; } launchOptions:nil];

RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) {
RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage, __unused NSValue *dimensions) {
XCTAssertEqualObjects(decodedImage, image);
XCTAssertNil(decodeError);
}];
Expand All @@ -122,7 +122,7 @@ - (void)testImageLoaderUsesImageDecoderWithHighestPriority
return YES;
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
XCTAssertEqualObjects(imageData, data);
completionHandler(nil, image);
completionHandler(nil, image, nil);
return nil;
}];

Expand All @@ -136,7 +136,7 @@ - (void)testImageLoaderUsesImageDecoderWithHighestPriority
RCTImageLoader *imageLoader = [RCTImageLoader new];
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder1, decoder2, imageLoader]; } launchOptions:nil];

RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) {
RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage, __unused NSValue *dimensions) {
XCTAssertEqualObjects(decodedImage, image);
XCTAssertNil(decodeError);
}];
Expand Down
6 changes: 3 additions & 3 deletions Libraries/CameraRoll/RCTAssetsLibraryImageLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,20 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
image = RCTScaledImageForAsset(representation, size, scale, resizeMode, &error);
}

completionHandler(error, image);
completionHandler(error, image, nil);
}
});
} else {
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageURL];
completionHandler(RCTErrorWithMessage(errorText), nil);
completionHandler(RCTErrorWithMessage(errorText), nil, nil);
}
} failureBlock:^(NSError *loadError) {
if (cancelled) {
return;
}

NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageURL, loadError];
completionHandler(RCTErrorWithMessage(errorText), nil);
completionHandler(RCTErrorWithMessage(errorText), nil, nil);
}];

return ^{
Expand Down
2 changes: 1 addition & 1 deletion Libraries/CameraRoll/RCTCameraRollManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ @implementation RCTCameraRollManager
successCallback:(RCTResponseSenderBlock)successCallback
errorCallback:(RCTResponseErrorBlock)errorCallback)
{
[_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *loadError, UIImage *loadedImage) {
[_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *loadError, UIImage *loadedImage, NSValue *dimensions) {
if (loadError) {
errorCallback(loadError);
return;
Expand Down
6 changes: 3 additions & 3 deletions Libraries/CameraRoll/RCTPhotoLibraryImageLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
PHFetchResult *results = [PHAsset fetchAssetsWithLocalIdentifiers:@[phAssetID] options:nil];
if (results.count == 0) {
NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID];
completionHandler(RCTErrorWithMessage(errorText), nil);
completionHandler(RCTErrorWithMessage(errorText), nil, nil);
return ^{};
}

Expand Down Expand Up @@ -83,9 +83,9 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
options:imageOptions
resultHandler:^(UIImage *result, NSDictionary *info) {
if (result) {
completionHandler(nil, result);
completionHandler(nil, result, nil);
} else {
completionHandler(info[PHImageErrorKey], nil);
completionHandler(info[PHImageErrorKey], nil, nil);
}
}];

Expand Down
3 changes: 2 additions & 1 deletion Libraries/Image/Image.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ var Image = React.createClass({
*/
onError: PropTypes.func,
/**
* Invoked when load completes successfully
* Invoked when load completes successfully. Network images
* will return with `{nativeEvent: {size}}`.
* @platform ios
*/
onLoad: PropTypes.func,
Expand Down
2 changes: 1 addition & 1 deletion Libraries/Image/RCTGIFImageDecoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
CFRelease(imageSource);
}

completionHandler(nil, image);
completionHandler(nil, image, nil);
return ^{};
}

Expand Down
2 changes: 1 addition & 1 deletion Libraries/Image/RCTImageEditingManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ @implementation RCTImageEditingManager
return;
}

[_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *error, UIImage *image) {
[_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *error, UIImage *image, NSValue *dimensions) {
if (error) {
errorCallback(error);
return;
Expand Down
2 changes: 1 addition & 1 deletion Libraries/Image/RCTImageLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@class ALAssetsLibrary;

typedef void (^RCTImageLoaderProgressBlock)(int64_t progress, int64_t total);
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, UIImage *image);
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, UIImage *image, NSValue *dimensions);
typedef void (^RCTImageLoaderCancellationBlock)(void);

@interface UIImage (React)
Expand Down
17 changes: 9 additions & 8 deletions Libraries/Image/RCTImageLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -183,18 +183,18 @@ - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
}

__block volatile uint32_t cancelled = 0;
RCTImageLoaderCompletionBlock completionHandler = ^(NSError *error, UIImage *image) {
RCTImageLoaderCompletionBlock completionHandler = ^(NSError *error, UIImage *image, NSValue *dimensions) {
if ([NSThread isMainThread]) {

// Most loaders do not return on the main thread, so caller is probably not
// expecting it, and may do expensive post-processing in the callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (!cancelled) {
completionBlock(error, image);
completionBlock(error, image, dimensions);
}
});
} else if (!cancelled) {
completionBlock(error, image);
completionBlock(error, image, dimensions);
}
};

Expand Down Expand Up @@ -232,7 +232,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
if (statusCode != 200) {
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
code:statusCode
userInfo:nil], nil);
userInfo:nil], nil, nil);
return;
}
}
Expand Down Expand Up @@ -264,7 +264,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
RCTNetworkTask *task = [_bridge.networking networkTaskWithRequest:request completionBlock:
^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
completionHandler(error, nil);
completionHandler(error, nil, nil);
return;
}

Expand Down Expand Up @@ -321,12 +321,13 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
return;
}
UIImage *image = [UIImage imageWithData:data scale:scale];
NSValue *intrinsicSize = [NSValue valueWithCGSize:image.size];
if (image) {
completionHandler(nil, image);
completionHandler(nil, image, intrinsicSize);
} else {
NSString *errorMessage = [NSString stringWithFormat:@"Error decoding image data <NSData %p; %tu bytes>", data, data.length];
NSError *finalError = RCTErrorWithMessage(errorMessage);
completionHandler(finalError, nil);
completionHandler(finalError, nil, nil);
}
});

Expand All @@ -353,7 +354,7 @@ - (BOOL)canHandleRequest:(NSURLRequest *)request
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)delegate
{
__block RCTImageLoaderCancellationBlock requestToken;
requestToken = [self loadImageWithTag:request.URL.absoluteString callback:^(NSError *error, UIImage *image) {
requestToken = [self loadImageWithTag:request.URL.absoluteString callback:^(NSError *error, UIImage *image, NSValue *dimensions) {
if (error) {
[delegate URLRequest:requestToken didCompleteWithError:error];
return;
Expand Down
4 changes: 2 additions & 2 deletions Libraries/Image/RCTImageStoreManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSiz
NSString *imageTag = imageURL.absoluteString;
[self getImageForTag:imageTag withBlock:^(UIImage *image) {
if (image) {
completionHandler(nil, image);
completionHandler(nil, image, nil);
} else {
NSString *errorMessage = [NSString stringWithFormat:@"Unable to load image from image store: %@", imageTag];
NSError *error = RCTErrorWithMessage(errorMessage);
completionHandler(error, nil);
completionHandler(error, nil, nil);
}
}];

Expand Down
15 changes: 13 additions & 2 deletions Libraries/Image/RCTImageView.m
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ - (void)reloadImage
scale:RCTScreenScale()
resizeMode:self.contentMode
progressBlock:progressHandler
completionBlock:^(NSError *error, UIImage *image) {
completionBlock:^(NSError *error, UIImage *image, NSValue *dimensions) {
dispatch_async(dispatch_get_main_queue(), ^{
if (image.reactKeyframeAnimation) {
[self.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
Expand All @@ -196,7 +196,18 @@ - (void)reloadImage
}
} else {
if (_onLoad) {
_onLoad(nil);
if (dimensions != nil) {
CGSize size = [dimensions CGSizeValue];
_onLoad(@{
@"size": @{
@"height": @(size.height),
@"width": @(size.width),
}
});
}
else {
_onLoad(nil);
}
}
}
if (_onLoadEnd) {
Expand Down
2 changes: 1 addition & 1 deletion Libraries/Image/RCTShadowVirtualImage.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ - (void)setSource:(NSDictionary *)source
scale:scale
resizeMode:UIViewContentModeScaleToFill
progressBlock:nil
completionBlock:^(NSError *error, UIImage *image) {
completionBlock:^(NSError *error, UIImage *image, NSValue *dimensions) {

dispatch_async(_bridge.uiManager.methodQueue, ^{
RCTShadowVirtualImage *strongSelf = weakSelf;
Expand Down
4 changes: 2 additions & 2 deletions Libraries/Image/RCTXCAssetImageLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
if (progressHandler) {
progressHandler(1, 1);
}
completionHandler(nil, image);
completionHandler(nil, image, nil);
} else {
NSString *message = [NSString stringWithFormat:@"Could not find image named %@", imageName];
completionHandler(RCTErrorWithMessage(message), nil);
completionHandler(RCTErrorWithMessage(message), nil, nil);
}
});

Expand Down

0 comments on commit 1e632f1

Please sign in to comment.