69

I would like to be able to know the actual size of a network-loaded image that has been passed into <Image /> I have tried using onLayout to work out the size (as taken from here https://github.com/facebook/react-native/issues/858) but that seems to return the sanitised size after it's already been pushed through the layout engine.

I tried looking into onLoadStart, onLoad, onLoadEnd, onProgress to see if there was any other information available but cannot seem to get any of these to fire. I have declared them as follows:

  onImageLoadStart: function(e){
    console.log("onImageLoadStart");
  },

  onImageLoad: function(e){
    console.log("onImageLoad");
  },

  onImageLoadEnd: function(e){
    console.log("onImageLoadEnd");
  },


  onImageProgress: function(e){
    console.log("onImageProgress");
  },

  onImageError: function(e){
    console.log("onImageError");
  },

  render: function (e) {
    return (
      <Image
        source={{uri: "http://adomain.com/myimageurl.jpg"}}
        style={[this.props.style, this.state.style]}
        onLayout={this.onImageLayout}
        onLoadStart={(e) => {this.onImageLoadStart(e)}}
        onLoad={(e) => {this.onImageLoad(e)}}
        onLoadEnd={(e) => {this.onImageLoadEnd(e)}}
        onProgress={(e) => {this.onImageProgress(e)}}
        onError={(e) => {this.onImageError(e)}} />
    );
  }

Thanks.

Moss Palmer
  • 1,814
  • 1
  • 15
  • 30

4 Answers4

172

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})});
Meysam Izadmehr
  • 3,103
  • 17
  • 25
Bill
  • 4,506
  • 2
  • 18
  • 29
  • 3
    If I use this to get the image dimensions and then set the image with it does it mean it fetch the image twice ? – rsanath May 03 '18 at 02:52
  • 1
    @JavaBanana If it does fetch it a second time, the Fetch API itself in react native has caching in it that will ostensibly prevent the second http request. In fact, I've had to disable that fetch caching in places because it is quite persistent. – lance.dolan May 15 '18 at 19:14
  • 12
    For static images, use Image.resolveAssetSource(your_static_image).width or Image.resolveAssetSource(your_static_image).height. Source: https://stackoverflow.com/questions/41997611/is-it-possible-to-using-image-getsize-with-static-image-file – Tiago Peres França Aug 09 '18 at 16:44
  • 1
    Is it possible to pass headers in this method? – ThinkAndCode Mar 24 '21 at 04:58
  • @ThinkAndCode yes, it is possible, but there's another method for that: https://reactnative.dev/docs/image#getsizewithheaders – Alexandru Ionuţ Bujor Aug 24 '23 at 08:40
41

You can use resolveAssetSource method from the Image component :

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

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.

Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
Moss Palmer
  • 1,814
  • 1
  • 15
  • 30
4

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));
  });
}
Ihor Kucherenko
  • 133
  • 2
  • 5