React Native Retrieve Actual Image Sizes

TypeScript example:

import {Image} from 'react-native';

export interface ISize {
  width: number;
  height: number;
}

function getImageSize(uri: string): Promise<ISize> {
  const success = (resolve: (value?: ISize | PromiseLike<ISize>) => void) => (width: number, height: number) => {
    resolve({
      width,
      height
    });
  };
  const error = (reject: (reason?: any) => void) => (failure: Error) => {
   reject(failure);
  };

  return new Promise<ISize>((resolve, reject) => {
    Image.getSize(uri, success(resolve), error(reject));
  });
}

This answer is now out of date. See Bill's answer.

Image.getSize(myUri, (width, height) => { this.setState({ width, height }) });

Old Answer (valid for older builds of react native)

Ok, I got it working. Currently this takes some modification of the React-Native installation as it's not natively supported.

I followed the tips in this thread to enabled me to do this. https://github.com/facebook/react-native/issues/494

Mainly, alter the RCTNetworkImageView.m file: add the following into setImageURL

void (^loadImageEndHandler)(UIImage *image) = ^(UIImage *image) {
  NSDictionary *event = @{
    @"target": self.reactTag,
    @"size": @{
      @"height": @(image.size.height),
      @"width": @(image.size.width)
    }
  };
  [_eventDispatcher sendInputEventWithName:@"loaded" body:event];
};

Then edit the line that handles the load completion:

[self.layer removeAnimationForKey:@"contents"];
self.layer.contentsScale = image.scale;
self.layer.contents = (__bridge id)image.CGImage;
loadEndHandler();

replace

loadEndHandler();

with

loadImageEndHandler(image);

Then in React-Native you have access to the size via the native events. data from the onLoaded function - note the documentation currently says the function is onLoad but this is incorrect. The correct functions are as follows for v0.8.0:

onLoadStart
onLoadProgress
onLoaded
onLoadError
onLoadAbort

These can be accessed like so:

onImageLoaded: function(data){
    try{
        console.log("image width:"+data.nativeEvents.size.width);
        console.log("image height:"+data.nativeEvents.size.height);
    }catch(e){
        //error
    }
},
...
render: function(){
    return (
        <View style={{width:1,height:1,overflow='hidden'}}>
            <Image source={{uri: yourImageURL}} resizeMode='contain' onLoaded={this.onImageLoaded} style={{width:5000,height:5000}} />
        </View>
    );
}

Points to note:

  • I have set a large image window and set it inside a wrapping element of 1x1px this is because the image must fit inside if you are to retrieve meaningful values.

  • The resize mode must be 'contain' to enable you to get the correct sizes, otherwise the constrained size will be reported.

  • The image sizes are scaled proportionately to the scale factor of the device, e.g. a 200*200 image on an iPhone6 (not 6 plus) will be reported as 100*100. I assume that this also means it will be reported as 67*67 on an iPhone6 plus but I have not tested this.

  • I have not yet got this to work for GIF files which traverse a different path on the Obj-C side of the bridge. I will update this answer once I have done that.

  • I believe there is a PR going through for this at the moment but until it is included in the core then this change will have to be made to the react-native installation every time you update/re-install.


Image component now provides a static method to get the size of the image. For example:

Image.getSize(myUri, (width, height) => {this.setState({width, height})});

You can use resolveAssetSource method from the Image component :

import picture from 'pathToYourPicture';  
const {width, height} = Image.resolveAssetSource(picture);

Tags:

React Native